blob: c72b511720e5816956b8d235fe3682995d111cd5 [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;
Jian Li4df657b2018-05-29 16:39:00 +090067import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090068import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090069import org.openstack4j.model.network.Port;
70import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090071import 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;
Hyunsun Moon0d457362017-06-27 17:19:41 +090078import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070079import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090080import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090081
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070082import static java.util.concurrent.Executors.newSingleThreadExecutor;
83import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090084import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
85import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
86import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
87import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Lif96685c2018-05-21 14:14:16 +090088import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
Jian Li60312252018-05-10 18:40:32 +090089import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
90import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090091import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090092import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
93import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
94import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Hyunsun Moon0d457362017-06-27 17:19:41 +090095import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +090096import static org.slf4j.LoggerFactory.getLogger;
97
98/**
Hyunsun Moon44aac662017-02-18 02:07:01 +090099 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900100 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700101@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900102public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700104 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900105
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
107 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900108 private static final String ARP_MODE = "arpMode";
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900112
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900115
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900117 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700118
Hyunsun Moon44aac662017-02-18 02:07:01 +0900119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900120 protected OpenstackRouterService osRouterService;
121
daniel parkeeb8e042018-02-21 14:06:58 +0900122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900123 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124
Jian Li60312252018-05-10 18:40:32 +0900125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected ClusterService clusterService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected LeadershipService leadershipService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected OpenstackFlowRuleService osFlowRuleService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected ComponentConfigService configService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected HostService hostService;
139
140 // TODO: need to find a way to unify aprMode and gatewayMac variables with
141 // that in SwitchingArpHandler
142 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Jian Li1478f292018-05-28 17:10:59 +0900143 label = "ARP processing mode, broadcast (default) | proxy ")
Jian Li60312252018-05-10 18:40:32 +0900144 protected String arpMode = DEFAULT_ARP_MODE_STR;
145
146 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
147
148 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
149 private final HostListener hostListener = new InternalHostListener();
Jian Lif96685c2018-05-21 14:14:16 +0900150 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li60312252018-05-10 18:40:32 +0900151
152 private ApplicationId appId;
153 private NodeId localNodeId;
154 private Map<String, String> floatingIpMacMap = Maps.newConcurrentMap();
155
Hyunsun Moon44aac662017-02-18 02:07:01 +0900156 private final ExecutorService eventExecutor = newSingleThreadExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700158
Hyunsun Moon0d457362017-06-27 17:19:41 +0900159 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700160
161 @Activate
162 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900163 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
164 configService.registerProperties(getClass());
165 localNodeId = clusterService.getLocalNode().id();
166 osRouterService.addListener(osRouterListener);
167 hostService.addListener(hostListener);
Jian Lif96685c2018-05-21 14:14:16 +0900168 osNodeService.addListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900169 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700170 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
171 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900172 }
173
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700174 @Deactivate
175 protected void deactivate() {
176 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900177 hostService.removeListener(hostListener);
178 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900179 osNodeService.removeListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900180 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900182 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700183 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900184 }
185
Jian Li60312252018-05-10 18:40:32 +0900186 // TODO: need to find a way to unify aprMode and gatewayMac variables with
187 // that in SwitchingArpHandler
188 @Modified
189 void modified(ComponentContext context) {
190 Dictionary<?, ?> properties = context.getProperties();
191 String updateArpMode;
192
193 updateArpMode = Tools.get(properties, ARP_MODE);
194 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
195 arpMode = updateArpMode;
196 }
197
198 log.info("Modified");
199 }
200
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700201 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900202 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900203
204 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900205 if (log.isTraceEnabled()) {
206 log.trace("ARP request received from {} for {}",
207 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
208 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
209 }
210
211 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900212
213 MacAddress targetMac = null;
214
215 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
216 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
217 .findAny().orElse(null);
218
daniel park576969a2018-03-09 07:07:41 +0900219 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900220 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900221 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
222 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900223 }
224
225 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
226 targetMac = Constants.DEFAULT_GATEWAY_MAC;
227 }
228
229 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900230 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
231 return;
232 }
233
daniel parkb5817102018-02-15 00:18:51 +0900234 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
235 targetMac, ethernet);
236
daniel park576969a2018-03-09 07:07:41 +0900237
daniel parkb5817102018-02-15 00:18:51 +0900238 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900239 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900240
241 packetService.emit(new DefaultOutboundPacket(
242 context.inPacket().receivedFrom().deviceId(),
243 treatment,
244 ByteBuffer.wrap(ethReply.serialize())));
245
246 context.block();
Jian Li60312252018-05-10 18:40:32 +0900247 }
248
249 if (arp.getOpCode() == ARP.OP_REPLY) {
daniel parkb5817102018-02-15 00:18:51 +0900250 PortNumber receivedPortNum = context.inPacket().receivedFrom().port();
251 log.debug("ARP reply ip: {}, mac: {}",
252 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
253 MacAddress.valueOf(arp.getSenderHardwareAddress()));
254 try {
255 if (receivedPortNum.equals(
Jian Li60312252018-05-10 18:40:32 +0900256 osNodeService.node(context.inPacket().receivedFrom()
257 .deviceId()).uplinkPortNum())) {
daniel park32b42202018-03-14 16:53:44 +0900258 osNetworkAdminService.updateExternalPeerRouterMac(
daniel parkb5817102018-02-15 00:18:51 +0900259 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
260 MacAddress.valueOf(arp.getSenderHardwareAddress()));
261 }
262 } catch (Exception e) {
263 log.error("Exception occurred because of {}", e.toString());
264 }
Daniel Park81a61a12016-02-26 08:24:44 +0900265 }
266
Daniel Park81a61a12016-02-26 08:24:44 +0900267 }
268
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700269 private class InternalPacketProcessor implements PacketProcessor {
270
271 @Override
272 public void process(PacketContext context) {
273 if (context.isHandled()) {
274 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900275 }
276
277 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
278 .stream().map(OpenstackNode::intgBridge)
279 .collect(Collectors.toSet());
280
281 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700282 // return if the packet is not from gateway nodes
283 return;
284 }
285
286 InboundPacket pkt = context.inPacket();
287 Ethernet ethernet = pkt.parsed();
288 if (ethernet != null &&
289 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900290 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700291 }
292 }
293 }
294
daniel parkeeb8e042018-02-21 14:06:58 +0900295 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900296 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900297 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900298 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900299 .flatMap(osPort -> osPort.getFixedIps().stream())
300 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900301 }
Jian Li60312252018-05-10 18:40:32 +0900302
303 // FIXME: need to find a way to invoke this method during node initialization
304 private void initFloatingIpMacMap() {
305 osRouterService.floatingIps().forEach(f -> {
306 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
307 Port port = osNetworkAdminService.port(f.getPortId());
308 if (port != null && port.getMacAddress() != null) {
309 floatingIpMacMap.put(f.getFloatingIpAddress(), port.getMacAddress());
310 }
311 }
312 });
313 }
314
315 /**
316 * Installs static ARP rules used in ARP BROAD_CAST mode.
317 * Note that, those rules will be only matched ARP_REQUEST packets,
318 * used for telling gateway node the mapped MAC address of requested IP,
319 * without the helps from controller.
320 *
321 * @param fip floating IP address
322 * @param install flow rule installation flag
323 */
324 private void setFloatingIpArpRule(NetFloatingIP fip, boolean install) {
325 if (arpMode.equals(ARP_BROADCAST_MODE)) {
326
327 if (fip == null) {
328 log.warn("Failed to set ARP broadcast rule for floating IP");
329 return;
330 }
331
332 String macString;
333
334 if (install) {
335 if (fip.getPortId() != null) {
336 macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
337 floatingIpMacMap.put(fip.getFloatingIpAddress(), macString);
338 } else {
339 log.trace("Unknown target ARP request for {}, ignore it",
340 fip.getFloatingIpAddress());
341 return;
342 }
343 } else {
344 macString = floatingIpMacMap.get(fip.getFloatingIpAddress());
345 }
346
347 MacAddress targetMac = MacAddress.valueOf(macString);
348
349 TrafficSelector selector = DefaultTrafficSelector.builder()
350 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
351 .matchArpOp(ARP.OP_REQUEST)
352 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
353 .build();
354
355 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
356 .setArpOp(ARP.OP_REPLY)
357 .setArpSha(targetMac)
358 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
359 .setOutput(PortNumber.IN_PORT)
360 .build();
361
362 osNodeService.completeNodes(GATEWAY).forEach(n ->
363 osFlowRuleService.setRule(
364 appId,
365 n.intgBridge(),
366 selector,
367 treatment,
368 PRIORITY_ARP_GATEWAY_RULE,
369 GW_COMMON_TABLE,
370 install
371 )
372 );
373
374 if (install) {
375 log.info("Install ARP Rule for Floating IP {}",
376 fip.getFloatingIpAddress());
377 } else {
378 log.info("Uninstall ARP Rule for Floating IP {}",
379 fip.getFloatingIpAddress());
380 }
381 }
382 }
383
384 /**
385 * An internal router event listener, intended to install/uninstall
386 * ARP rules for forwarding packets created from floating IPs.
387 */
388 private class InternalRouterEventListener implements OpenstackRouterListener {
389
390 @Override
391 public boolean isRelevant(OpenstackRouterEvent event) {
392 // do not allow to proceed without leadership
393 NodeId leader = leadershipService.getLeader(appId.name());
394 return Objects.equals(localNodeId, leader);
395 }
396
397 @Override
398 public void event(OpenstackRouterEvent event) {
399 switch (event.type()) {
400 case OPENSTACK_ROUTER_CREATED:
401 eventExecutor.execute(() ->
402 // add a router with external gateway
403 setFakeGatewayArpRule(event.subject(), true)
404 );
405 break;
406 case OPENSTACK_ROUTER_REMOVED:
407 eventExecutor.execute(() ->
408 // remove a router with external gateway
409 setFakeGatewayArpRule(event.subject(), false)
410 );
411 break;
412 case OPENSTACK_ROUTER_GATEWAY_ADDED:
413 eventExecutor.execute(() ->
414 // add a gateway manually after adding a router
415 setFakeGatewayArpRule(event.externalGateway(), true)
416 );
417 break;
418 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
419 eventExecutor.execute(() ->
420 // remove a gateway from an existing router
421 setFakeGatewayArpRule(event.externalGateway(), false)
422 );
423 break;
424 case OPENSTACK_FLOATING_IP_ASSOCIATED:
425 eventExecutor.execute(() ->
426 // associate a floating IP with an existing VM
427 setFloatingIpArpRule(event.floatingIp(), true)
428 );
429 break;
430 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
431 eventExecutor.execute(() ->
432 // disassociate a floating IP with the existing VM
433 setFloatingIpArpRule(event.floatingIp(), false)
434 );
435 break;
436 case OPENSTACK_FLOATING_IP_CREATED:
437 eventExecutor.execute(() -> {
438 NetFloatingIP osFip = event.floatingIp();
439
440 // during floating IP creation, if the floating IP is
441 // associated with any port of VM, then we will set
442 // floating IP related ARP rules to gateway node
443 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
444 setFloatingIpArpRule(osFip, true);
445 }
446 });
447 break;
448 case OPENSTACK_FLOATING_IP_REMOVED:
449 eventExecutor.execute(() -> {
450 NetFloatingIP osFip = event.floatingIp();
451
452 // during floating IP deletion, if the floating IP is
453 // still associated with any port of VM, then we will
454 // remove floating IP related ARP rules from gateway node
455 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
456 setFloatingIpArpRule(event.floatingIp(), false);
457 }
458 });
459 break;
460 default:
461 // do nothing for the other events
462 break;
463 }
464 }
465
Jian Li4df657b2018-05-29 16:39:00 +0900466 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
467 return osNetworkAdminService.ports().stream()
468 .filter(port ->
469 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
470 .filter(port ->
471 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
472 .flatMap(port -> port.getFixedIps().stream())
473 .collect(Collectors.toSet());
474 }
475
Jian Li60312252018-05-10 18:40:32 +0900476 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
477 if (arpMode.equals(ARP_BROADCAST_MODE)) {
478
479 if (extGw == null) {
480 return;
481 }
482
Jian Li4df657b2018-05-29 16:39:00 +0900483 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900484
Jian Li4df657b2018-05-29 16:39:00 +0900485 ips.forEach(ip -> {
486 TrafficSelector selector = DefaultTrafficSelector.builder()
487 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
488 .matchArpOp(ARP.OP_REQUEST)
489 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
490 .build();
Jian Li60312252018-05-10 18:40:32 +0900491
Jian Li4df657b2018-05-29 16:39:00 +0900492 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
493 .setArpOp(ARP.OP_REPLY)
494 .setArpSha(MacAddress.valueOf(gatewayMac))
495 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
496 .setOutput(PortNumber.IN_PORT)
497 .build();
Jian Li60312252018-05-10 18:40:32 +0900498
Jian Li4df657b2018-05-29 16:39:00 +0900499 osNodeService.completeNodes(GATEWAY).forEach(n ->
500 osFlowRuleService.setRule(
501 appId,
502 n.intgBridge(),
503 selector,
504 treatment,
505 PRIORITY_ARP_GATEWAY_RULE,
506 GW_COMMON_TABLE,
507 install
508 )
509 );
Jian Li60312252018-05-10 18:40:32 +0900510
Jian Li4df657b2018-05-29 16:39:00 +0900511 if (install) {
512 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
513 } else {
514 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
515 }
516 });
Jian Li60312252018-05-10 18:40:32 +0900517 }
518 }
519
520 private void setFakeGatewayArpRule(Router router, boolean install) {
521 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
522 }
523 }
524
525 /**
526 * An internal host event listener, intended to uninstall
527 * ARP rules during host removal. Note that this is only valid when users
528 * remove host without disassociating floating IP with existing VM.
529 */
530 private class InternalHostListener implements HostListener {
531
532 @Override
533 public boolean isRelevant(HostEvent event) {
534 Host host = event.subject();
535 if (!isValidHost(host)) {
536 log.debug("Invalid host detected, ignore it {}", host);
537 return false;
538 }
539 return true;
540 }
541
542 @Override
543 public void event(HostEvent event) {
544 InstancePort instPort = HostBasedInstancePort.of(event.subject());
545 switch (event.type()) {
546 case HOST_REMOVED:
547 removeArpRuleByInstancePort(instPort);
548 break;
549 case HOST_UPDATED:
550 case HOST_ADDED:
551 default:
552 break;
553 }
554 }
555
556 private void removeArpRuleByInstancePort(InstancePort port) {
557 Set<NetFloatingIP> ips = osRouterService.floatingIps();
558 for (NetFloatingIP fip : ips) {
559 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
560 continue;
561 }
562 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
563 continue;
564 }
565 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
566 eventExecutor.execute(() ->
567 setFloatingIpArpRule(fip, false)
568 );
569 }
570 }
571 }
572
573 // TODO: should be extracted as an utility helper method sooner
574 private boolean isValidHost(Host host) {
575 return !host.ipAddresses().isEmpty() &&
576 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
577 host.annotations().value(ANNOTATION_PORT_ID) != null;
578 }
579 }
Jian Lif96685c2018-05-21 14:14:16 +0900580
581 private class InternalNodeEventListener implements OpenstackNodeListener {
582
583 @Override
584 public boolean isRelevant(OpenstackNodeEvent event) {
585 // do not allow to proceed without leadership
586 NodeId leader = leadershipService.getLeader(appId.name());
587 return Objects.equals(localNodeId, leader);
588 }
589
590 @Override
591 public void event(OpenstackNodeEvent event) {
592 OpenstackNode osNode = event.subject();
593 switch (event.type()) {
594 case OPENSTACK_NODE_COMPLETE:
595 if (osNode.type().equals(GATEWAY)) {
596 setDefaultArpRule(osNode, true);
597 }
598 break;
599 case OPENSTACK_NODE_INCOMPLETE:
600 if (osNode.type().equals(GATEWAY)) {
601 setDefaultArpRule(osNode, false);
602 }
603 break;
604 default:
605 break;
606 }
607 }
608
609 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
610 switch (arpMode) {
611 case ARP_PROXY_MODE:
612 setDefaultArpRuleForProxyMode(osNode, install);
613 break;
614 case ARP_BROADCAST_MODE:
615 setDefaultArpRuleForBroadcastMode(osNode, install);
616 break;
617 default:
618 log.warn("Invalid ARP mode {}. Please use either " +
619 "broadcast or proxy mode.", arpMode);
620 break;
621 }
622 }
623
624 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
625 TrafficSelector selector = DefaultTrafficSelector.builder()
626 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
627 .build();
628
629 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
630 .punt()
631 .build();
632
633 osFlowRuleService.setRule(
634 appId,
635 osNode.intgBridge(),
636 selector,
637 treatment,
638 PRIORITY_ARP_CONTROL_RULE,
639 DHCP_ARP_TABLE,
640 install
641 );
642 }
643
644 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
645 // we only match ARP_REPLY in gateway node, because controller
646 // somehow need to process ARP_REPLY which is issued from
647 // external router...
648 TrafficSelector selector = DefaultTrafficSelector.builder()
649 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
650 .matchArpOp(ARP.OP_REPLY)
651 .build();
652
653 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
654 .punt()
655 .build();
656
657 osFlowRuleService.setRule(
658 appId,
659 osNode.intgBridge(),
660 selector,
661 treatment,
662 PRIORITY_ARP_CONTROL_RULE,
663 DHCP_ARP_TABLE,
664 install
665 );
666 }
667 }
Daniel Park81a61a12016-02-26 08:24:44 +0900668}