blob: a6f54bd5578f4cae54b4081a5c2bb26fae3f2bae [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 Lida03ce92018-07-24 21:41:53 +090018import com.google.common.base.Strings;
Jian Li1064e4f2018-05-29 16:16:53 +090019import com.google.common.collect.ImmutableSet;
Jian Li1064e4f2018-05-29 16:16:53 +090020import com.google.common.collect.Sets;
Daniel Park81a61a12016-02-26 08:24:44 +090021import org.onlab.packet.ARP;
Jian Li60312252018-05-10 18:40:32 +090022import org.onlab.packet.EthType;
Daniel Park81a61a12016-02-26 08:24:44 +090023import org.onlab.packet.Ethernet;
Daniel Park81a61a12016-02-26 08:24:44 +090024import org.onlab.packet.Ip4Address;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
Jian Li60312252018-05-10 18:40:32 +090027import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090028import org.onosproject.cfg.ConfigProperty;
Jian Li60312252018-05-10 18:40:32 +090029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Jian Li14a79f22018-06-05 03:44:22 +090034import org.onosproject.net.ConnectPoint;
Hyunsun Moon0d457362017-06-27 17:19:41 +090035import org.onosproject.net.DeviceId;
daniel parkb5817102018-02-15 00:18:51 +090036import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090037import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090038import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090039import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090040import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070042import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090043import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070044import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090045import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090046import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090047import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090048import org.onosproject.openstacknetworking.api.InstancePortEvent;
49import org.onosproject.openstacknetworking.api.InstancePortListener;
Jian Li1064e4f2018-05-29 16:16:53 +090050import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090051import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090052import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090053import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090054import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
55import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090056import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090057import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090058import org.onosproject.openstacknode.api.OpenstackNodeEvent;
59import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090060import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090061import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090062import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090063import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090064import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090065import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070066import org.osgi.service.component.annotations.Activate;
67import org.osgi.service.component.annotations.Component;
68import org.osgi.service.component.annotations.Deactivate;
69import org.osgi.service.component.annotations.Modified;
70import org.osgi.service.component.annotations.Reference;
71import org.osgi.service.component.annotations.ReferenceCardinality;
Daniel Park81a61a12016-02-26 08:24:44 +090072import org.slf4j.Logger;
73
74import java.nio.ByteBuffer;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090076import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070077import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090078import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090079
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070080import static java.util.concurrent.Executors.newSingleThreadExecutor;
81import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090082import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
83import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
84import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
85import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
86import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
87import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090088import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090089import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
Jian Li24ec59f2018-05-23 19:01:25 +090090import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
91import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Lia171a432018-06-11 11:52:11 +090092import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Jian Li7f70bb72018-07-06 23:35:30 +090093import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Li24ec59f2018-05-23 19:01:25 +090094import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Jian Liec5c32b2018-07-13 14:28:58 +090095import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
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
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li60312252018-05-10 18:40:32 +0900112 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700115 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park32b42202018-03-14 16:53:44 +0900118 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel parkeeb8e042018-02-21 14:06:58 +0900121 protected OpenstackRouterService osRouterService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel parke49eb382017-04-05 16:48:28 +0900124 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li1064e4f2018-05-29 16:16:53 +0900127 protected InstancePortService instancePortService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li60312252018-05-10 18:40:32 +0900130 protected ClusterService clusterService;
131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li60312252018-05-10 18:40:32 +0900133 protected LeadershipService leadershipService;
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li60312252018-05-10 18:40:32 +0900136 protected OpenstackFlowRuleService osFlowRuleService;
137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li1064e4f2018-05-29 16:16:53 +0900139 protected OpenstackNetworkService osNetworkService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lif3a28b02018-06-11 21:29:13 +0900142 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 //@Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
145 // label = "ARP processing mode, broadcast | proxy (default)")
Jian Li60312252018-05-10 18:40:32 +0900146 protected String arpMode = DEFAULT_ARP_MODE_STR;
147
148 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
149
150 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
Jian Lif96685c2018-05-21 14:14:16 +0900151 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li24ec59f2018-05-23 19:01:25 +0900152 private final InstancePortListener instPortListener = new InternalInstancePortListener();
Jian Li60312252018-05-10 18:40:32 +0900153
154 private ApplicationId appId;
155 private NodeId localNodeId;
Jian Liec5c32b2018-07-13 14:28:58 +0900156
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();
Jian Lif3a28b02018-06-11 21:29:13 +0900167 osRouterService.addListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900168 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900169 instancePortService.addListener(instPortListener);
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 Lie1a39032018-06-19 21:49:36 +0900178 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900179 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900180 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900181 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900182 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900183 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900184 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700185 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900186 }
187
Jian Li60312252018-05-10 18:40:32 +0900188 @Modified
189 void modified(ComponentContext context) {
Jian Li60312252018-05-10 18:40:32 +0900190 log.info("Modified");
191 }
192
Jian Li7f70bb72018-07-06 23:35:30 +0900193 private String getArpMode() {
194 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
195 return getPropertyValue(properties, ARP_MODE);
196 }
197
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700198 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900199 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900200
Jian Li7f70bb72018-07-06 23:35:30 +0900201 if (arp.getOpCode() == ARP.OP_REQUEST && ARP_PROXY_MODE.equals(getArpMode())) {
daniel parkb5817102018-02-15 00:18:51 +0900202 if (log.isTraceEnabled()) {
203 log.trace("ARP request received from {} for {}",
204 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
205 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
206 }
207
208 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900209
210 MacAddress targetMac = null;
211
212 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
213 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
214 .findAny().orElse(null);
215
daniel park576969a2018-03-09 07:07:41 +0900216 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900217 if (floatingIP != null && floatingIP.getPortId() != null) {
Daniel Park96f1e032018-08-09 13:30:57 +0900218 InstancePort instPort = instancePortService.instancePort(floatingIP.getPortId());
219 if (instPort == null) {
220 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
221 return;
222 } else {
223 targetMac = instPort.macAddress();
224 }
225
226 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
227
228 if (gw == null) {
229 return;
230 }
231
232 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
233 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
234 return;
235 }
daniel parkeeb8e042018-02-21 14:06:58 +0900236 }
237
Daniel Park96f1e032018-08-09 13:30:57 +0900238 if (isExternalGatewaySourceIp(targetIp)) {
daniel parkeeb8e042018-02-21 14:06:58 +0900239 targetMac = Constants.DEFAULT_GATEWAY_MAC;
240 }
241
242 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900243 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
244 return;
245 }
246
daniel parkb5817102018-02-15 00:18:51 +0900247 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
248 targetMac, ethernet);
249
250 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900251 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900252
253 packetService.emit(new DefaultOutboundPacket(
254 context.inPacket().receivedFrom().deviceId(),
255 treatment,
256 ByteBuffer.wrap(ethReply.serialize())));
257
258 context.block();
Jian Li60312252018-05-10 18:40:32 +0900259 }
260
261 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900262 ConnectPoint cp = context.inPacket().receivedFrom();
263 PortNumber receivedPortNum = cp.port();
264 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
265 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
266
267 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
268
daniel parkb5817102018-02-15 00:18:51 +0900269 try {
Jian Li14a79f22018-06-05 03:44:22 +0900270
Jian Lid4066ea2018-06-07 01:44:45 +0900271 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
Jian Li5e2ad4a2018-07-16 13:40:53 +0900272 stream().map(r -> r.ipAddress().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900273
Jian Lid4066ea2018-06-07 01:44:45 +0900274 // if SPA is NOT contained in existing external router IP set, we ignore it
275 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900276 return;
277 }
278
279 OpenstackNode node = osNodeService.node(cp.deviceId());
280
281 if (node == null) {
282 return;
283 }
284
285 // we only handles the ARP-Reply message received by gateway node
286 if (node.type() != GATEWAY) {
287 return;
288 }
289
290 if (receivedPortNum.equals(node.uplinkPortNum())) {
291 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900292 }
293 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900294 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900295 }
Daniel Park81a61a12016-02-26 08:24:44 +0900296 }
Daniel Park81a61a12016-02-26 08:24:44 +0900297 }
298
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700299 private class InternalPacketProcessor implements PacketProcessor {
300
301 @Override
302 public void process(PacketContext context) {
303 if (context.isHandled()) {
304 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900305 }
306
307 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
308 .stream().map(OpenstackNode::intgBridge)
309 .collect(Collectors.toSet());
310
311 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700312 // return if the packet is not from gateway nodes
313 return;
314 }
315
316 InboundPacket pkt = context.inPacket();
317 Ethernet ethernet = pkt.parsed();
318 if (ethernet != null &&
319 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900320 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700321 }
322 }
323 }
324
daniel parkeeb8e042018-02-21 14:06:58 +0900325 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900326 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900327 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900328 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900329 .flatMap(osPort -> osPort.getFixedIps().stream())
330 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900331 }
Jian Li60312252018-05-10 18:40:32 +0900332
Jian Li60312252018-05-10 18:40:32 +0900333 /**
334 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900335 *
336 * @param gateway gateway node
337 * @param install flow rule installation flag
338 */
339 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900340 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900341
342 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
343 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
344 finalGws.addAll(ImmutableSet.copyOf(completedGws));
345
346 if (install) {
347 if (completedGws.contains(gateway)) {
348 if (completedGws.size() > 1) {
349 finalGws.remove(gateway);
350 osRouterService.floatingIps().forEach(fip -> {
351 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900352 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900353 finalGws.add(gateway);
354 }
355 });
356 }
357 osRouterService.floatingIps().forEach(fip -> {
358 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900359 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900360 }
361 });
362 } else {
363 log.warn("Detected node should be included in completed gateway set");
364 }
365 } else {
366 if (!completedGws.contains(gateway)) {
367 finalGws.add(gateway);
368 osRouterService.floatingIps().forEach(fip -> {
369 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900370 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900371 }
372 });
373 finalGws.remove(gateway);
374 if (completedGws.size() >= 1) {
375 osRouterService.floatingIps().forEach(fip -> {
376 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900377 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900378 }
379 });
380 }
381 } else {
382 log.warn("Detected node should NOT be included in completed gateway set");
383 }
384 }
385 }
386 }
387
388 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900389 * Installs/uninstalls ARP flow rules to the corresponding gateway by
390 * looking for compute node's device ID.
391 *
392 * @param fip floating IP
393 * @param port instance port
394 * @param gateways a collection of gateways
395 * @param install install flag
396 */
397 private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
398 InstancePort port,
399 Set<OpenstackNode> gateways,
400 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900401 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li24ec59f2018-05-23 19:01:25 +0900402
403 OpenstackNode gw = getGwByInstancePort(gateways, port);
404
405 if (gw == null) {
406 return;
407 }
408
409 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
410
411 setArpRule(fip, MacAddress.valueOf(macString), gw, install);
412 }
413 }
414
415 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900416 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900417 * Note that, those rules will be only matched ARP_REQUEST packets,
418 * used for telling gateway node the mapped MAC address of requested IP,
419 * without the helps from controller.
420 *
421 * @param fip floating IP address
Jian Li8f64feb2018-07-24 13:20:16 +0900422 * @param portId port identifier
Jian Li1064e4f2018-05-29 16:16:53 +0900423 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900424 * @param install flow rule installation flag
425 */
Jian Li8f64feb2018-07-24 13:20:16 +0900426 private synchronized void setFloatingIpArpRule(NetFloatingIP fip, String portId,
Jian Li1064e4f2018-05-29 16:16:53 +0900427 Set<OpenstackNode> gateways,
428 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900429 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900430
431 if (fip == null) {
432 log.warn("Failed to set ARP broadcast rule for floating IP");
433 return;
434 }
435
Jian Lida03ce92018-07-24 21:41:53 +0900436 if (portId == null || fip.getPortId() == null) {
437 log.trace("Unknown target ARP request for {}, ignore it",
438 fip.getFloatingIpAddress());
439 return;
440 }
441
Jian Li8f64feb2018-07-24 13:20:16 +0900442 InstancePort instPort = instancePortService.instancePort(portId);
Jian Li46b74002018-07-15 18:39:08 +0900443 MacAddress targetMac = instPort.macAddress();
Jian Lif3a28b02018-06-11 21:29:13 +0900444
Jian Lia171a432018-06-11 11:52:11 +0900445 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900446
447 if (gw == null) {
448 return;
449 }
450
Jian Li24ec59f2018-05-23 19:01:25 +0900451 setArpRule(fip, targetMac, gw, install);
452 }
453 }
Jian Li60312252018-05-10 18:40:32 +0900454
Jian Li24ec59f2018-05-23 19:01:25 +0900455 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
456 OpenstackNode gateway, boolean install) {
457 TrafficSelector selector = DefaultTrafficSelector.builder()
458 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
459 .matchArpOp(ARP.OP_REQUEST)
460 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
461 .build();
Jian Li60312252018-05-10 18:40:32 +0900462
Jian Li24ec59f2018-05-23 19:01:25 +0900463 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
464 .setArpOp(ARP.OP_REPLY)
465 .setArpSha(targetMac)
466 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
467 .setOutput(PortNumber.IN_PORT)
468 .build();
Jian Li60312252018-05-10 18:40:32 +0900469
Jian Li24ec59f2018-05-23 19:01:25 +0900470 osFlowRuleService.setRule(
471 appId,
472 gateway.intgBridge(),
473 selector,
474 treatment,
475 PRIORITY_ARP_GATEWAY_RULE,
476 GW_COMMON_TABLE,
477 install
478 );
479
480 if (install) {
481 log.info("Install ARP Rule for Floating IP {}",
482 fip.getFloatingIpAddress());
483 } else {
484 log.info("Uninstall ARP Rule for Floating IP {}",
485 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900486 }
487 }
488
Daniel Park96f1e032018-08-09 13:30:57 +0900489 private void setFakeGatewayArpRule(Router router, boolean install) {
490 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
491 }
492
493 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
494 return osNetworkAdminService.ports().stream()
495 .filter(port ->
496 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
497 .filter(port ->
498 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
499 .flatMap(port -> port.getFixedIps().stream())
500 .collect(Collectors.toSet());
501 }
502
503 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
504 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
505
506 if (extGw == null) {
507 return;
508 }
509
510 Set<IP> ips = getExternalGatewaySnatIps(extGw);
511
512 ips.forEach(ip -> {
513 TrafficSelector selector = DefaultTrafficSelector.builder()
514 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
515 .matchArpOp(ARP.OP_REQUEST)
516 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
517 .build();
518
519 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
520 .setArpOp(ARP.OP_REPLY)
521 .setArpSha(MacAddress.valueOf(gatewayMac))
522 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
523 .setOutput(PortNumber.IN_PORT)
524 .build();
525
526 osNodeService.completeNodes(GATEWAY).forEach(n ->
527 osFlowRuleService.setRule(
528 appId,
529 n.intgBridge(),
530 selector,
531 treatment,
532 PRIORITY_ARP_GATEWAY_RULE,
533 GW_COMMON_TABLE,
534 install
535 )
536 );
537
538 if (install) {
539 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
540 } else {
541 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
542 }
543 });
544 }
545 }
546
Jian Li60312252018-05-10 18:40:32 +0900547 /**
548 * An internal router event listener, intended to install/uninstall
549 * ARP rules for forwarding packets created from floating IPs.
550 */
551 private class InternalRouterEventListener implements OpenstackRouterListener {
552
553 @Override
554 public boolean isRelevant(OpenstackRouterEvent event) {
555 // do not allow to proceed without leadership
556 NodeId leader = leadershipService.getLeader(appId.name());
557 return Objects.equals(localNodeId, leader);
558 }
559
560 @Override
561 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900562
563 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
564
Jian Li60312252018-05-10 18:40:32 +0900565 switch (event.type()) {
566 case OPENSTACK_ROUTER_CREATED:
567 eventExecutor.execute(() ->
568 // add a router with external gateway
569 setFakeGatewayArpRule(event.subject(), true)
570 );
571 break;
572 case OPENSTACK_ROUTER_REMOVED:
573 eventExecutor.execute(() ->
574 // remove a router with external gateway
575 setFakeGatewayArpRule(event.subject(), false)
576 );
577 break;
578 case OPENSTACK_ROUTER_GATEWAY_ADDED:
579 eventExecutor.execute(() ->
580 // add a gateway manually after adding a router
581 setFakeGatewayArpRule(event.externalGateway(), true)
582 );
583 break;
584 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
585 eventExecutor.execute(() ->
586 // remove a gateway from an existing router
587 setFakeGatewayArpRule(event.externalGateway(), false)
588 );
589 break;
590 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Jian Lida03ce92018-07-24 21:41:53 +0900591 if (getValidPortId(event) != null) {
592 eventExecutor.execute(() -> {
593 // associate a floating IP with an existing VM
594 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
595 completedGws, true);
596 });
597 }
Jian Li60312252018-05-10 18:40:32 +0900598 break;
599 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
Jian Lida03ce92018-07-24 21:41:53 +0900600 if (getValidPortId(event) != null) {
601 eventExecutor.execute(() -> {
602 // disassociate a floating IP with an existing VM
603 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
604 completedGws, false);
605 });
606 }
Jian Li60312252018-05-10 18:40:32 +0900607 break;
608 case OPENSTACK_FLOATING_IP_CREATED:
Jian Lida03ce92018-07-24 21:41:53 +0900609 // during floating IP creation, if the floating IP is
610 // associated with any port of VM, then we will set
611 // floating IP related ARP rules to gateway node
612 if (getValidPortId(event) != null) {
613 eventExecutor.execute(() -> {
614 // associate a floating IP with an existing VM
615 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
616 completedGws, true);
617 });
618 }
Jian Li60312252018-05-10 18:40:32 +0900619 break;
620 case OPENSTACK_FLOATING_IP_REMOVED:
Jian Lida03ce92018-07-24 21:41:53 +0900621 // during floating IP deletion, if the floating IP is
622 // still associated with any port of VM, then we will
623 // remove floating IP related ARP rules from gateway node
624 if (getValidPortId(event) != null) {
625 eventExecutor.execute(() -> {
626 // associate a floating IP with an existing VM
627 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
628 completedGws, false);
629 });
630 }
Jian Li60312252018-05-10 18:40:32 +0900631 break;
632 default:
633 // do nothing for the other events
634 break;
635 }
636 }
637
Jian Lida03ce92018-07-24 21:41:53 +0900638 private String getValidPortId(OpenstackRouterEvent event) {
639 NetFloatingIP osFip = event.floatingIp();
640 String portId = osFip.getPortId();
641
642 if (Strings.isNullOrEmpty(portId)) {
643 portId = event.portId();
644 }
645
646 if (portId != null && instancePortService.instancePort(portId) != null) {
647 return portId;
648 }
649
650 return null;
651 }
Jian Li60312252018-05-10 18:40:32 +0900652 }
653
Jian Lie1a39032018-06-19 21:49:36 +0900654 private class InternalInstancePortListener implements InstancePortListener {
Jian Li60312252018-05-10 18:40:32 +0900655
656 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900657 public boolean isRelevant(InstancePortEvent event) {
658 // do not allow to proceed without leadership
659 NodeId leader = leadershipService.getLeader(appId.name());
660 return Objects.equals(localNodeId, leader);
Jian Li60312252018-05-10 18:40:32 +0900661 }
662
663 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900664 public void event(InstancePortEvent event) {
665 InstancePort instPort = event.subject();
666
667 Set<NetFloatingIP> ips = osRouterService.floatingIps();
668 NetFloatingIP fip = associatedFloatingIp(instPort, ips);
669 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
670
Jian Li60312252018-05-10 18:40:32 +0900671 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900672 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Lie1a39032018-06-19 21:49:36 +0900673
Jian Li46b74002018-07-15 18:39:08 +0900674 osRouterService.floatingIps().stream()
675 .filter(f -> f.getPortId() != null)
676 .filter(f -> f.getPortId().equals(instPort.portId()))
Jian Li8f64feb2018-07-24 13:20:16 +0900677 .forEach(f -> setFloatingIpArpRule(f, instPort.portId(), gateways, true));
Jian Lie1a39032018-06-19 21:49:36 +0900678
Jian Li60312252018-05-10 18:40:32 +0900679 break;
Jian Lie1a39032018-06-19 21:49:36 +0900680 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
681
682 if (gateways.size() == 1) {
683 return;
684 }
685
686 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900687 eventExecutor.execute(() ->
Jian Lie1a39032018-06-19 21:49:36 +0900688 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
Jian Liec5c32b2018-07-13 14:28:58 +0900689 gateways, true)
690 );
Jian Lie1a39032018-06-19 21:49:36 +0900691 }
692
693 break;
694 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
695
Jian Liec5c32b2018-07-13 14:28:58 +0900696 InstancePort revisedInstPort = swapStaleLocation(event.subject());
697
Jian Lie1a39032018-06-19 21:49:36 +0900698 if (gateways.size() == 1) {
699 return;
700 }
701
702 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900703 DeviceId newDeviceId = event.subject().deviceId();
704 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Lie1a39032018-06-19 21:49:36 +0900705
706 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
707 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
708
709 if (oldGw != null && oldGw.equals(newGw)) {
710 return;
711 }
712
713 eventExecutor.execute(() ->
Jian Liec5c32b2018-07-13 14:28:58 +0900714 setFloatingIpArpRuleWithPortEvent(fip,
715 revisedInstPort, gateways, false));
Jian Lie1a39032018-06-19 21:49:36 +0900716 }
717 break;
718 default:
719 break;
720 }
Jian Li60312252018-05-10 18:40:32 +0900721 }
722 }
Jian Lif96685c2018-05-21 14:14:16 +0900723
724 private class InternalNodeEventListener implements OpenstackNodeListener {
725
726 @Override
727 public boolean isRelevant(OpenstackNodeEvent event) {
728 // do not allow to proceed without leadership
729 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900730 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900731 }
732
733 @Override
734 public void event(OpenstackNodeEvent event) {
735 OpenstackNode osNode = event.subject();
736 switch (event.type()) {
737 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900738 setDefaultArpRule(osNode, true);
739 setFloatingIpArpRuleForGateway(osNode, true);
Jian Lif96685c2018-05-21 14:14:16 +0900740 break;
741 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900742 setDefaultArpRule(osNode, false);
743 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900744 break;
745 default:
746 break;
747 }
748 }
749
750 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900751 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900752 case ARP_PROXY_MODE:
753 setDefaultArpRuleForProxyMode(osNode, install);
754 break;
755 case ARP_BROADCAST_MODE:
756 setDefaultArpRuleForBroadcastMode(osNode, install);
757 break;
758 default:
759 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900760 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900761 break;
762 }
763 }
764
765 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
766 TrafficSelector selector = DefaultTrafficSelector.builder()
767 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
768 .build();
769
770 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
771 .punt()
772 .build();
773
774 osFlowRuleService.setRule(
775 appId,
776 osNode.intgBridge(),
777 selector,
778 treatment,
779 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900780 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900781 install
782 );
783 }
784
785 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
786 // we only match ARP_REPLY in gateway node, because controller
787 // somehow need to process ARP_REPLY which is issued from
788 // external router...
789 TrafficSelector selector = DefaultTrafficSelector.builder()
790 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
791 .matchArpOp(ARP.OP_REPLY)
792 .build();
793
794 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
795 .punt()
796 .build();
797
798 osFlowRuleService.setRule(
799 appId,
800 osNode.intgBridge(),
801 selector,
802 treatment,
803 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900804 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900805 install
806 );
Daniel Park96f1e032018-08-09 13:30:57 +0900807
808 osRouterService.routers().stream()
809 .filter(router -> router.getExternalGatewayInfo() != null)
810 .forEach(router -> setFakeGatewayArpRule(router, install));
Jian Lif96685c2018-05-21 14:14:16 +0900811 }
812 }
Daniel Park81a61a12016-02-26 08:24:44 +0900813}