blob: 1f73566a993363e64a2e8920aa51a1089069f889 [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;
Jian Li1064e4f2018-05-29 16:16:53 +090019import com.google.common.collect.ImmutableSet;
Jian Li60312252018-05-10 18:40:32 +090020import com.google.common.collect.Maps;
Jian Li1064e4f2018-05-29 16:16:53 +090021import com.google.common.collect.Sets;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Jian Li60312252018-05-10 18:40:32 +090025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Daniel Park81a61a12016-02-26 08:24:44 +090029import org.onlab.packet.ARP;
Jian Li60312252018-05-10 18:40:32 +090030import org.onlab.packet.EthType;
Daniel Park81a61a12016-02-26 08:24:44 +090031import org.onlab.packet.Ethernet;
Daniel Park81a61a12016-02-26 08:24:44 +090032import org.onlab.packet.Ip4Address;
33import org.onlab.packet.IpAddress;
34import org.onlab.packet.MacAddress;
Jian Li60312252018-05-10 18:40:32 +090035import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090036import org.onosproject.cfg.ConfigProperty;
Jian Li60312252018-05-10 18:40:32 +090037import org.onosproject.cluster.ClusterService;
38import org.onosproject.cluster.LeadershipService;
39import org.onosproject.cluster.NodeId;
40import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
Jian Li14a79f22018-06-05 03:44:22 +090042import org.onosproject.net.ConnectPoint;
Hyunsun Moon0d457362017-06-27 17:19:41 +090043import org.onosproject.net.DeviceId;
daniel parkb5817102018-02-15 00:18:51 +090044import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090045import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090046import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090047import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090048import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070050import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090051import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070052import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090053import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090054import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090055import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090056import org.onosproject.openstacknetworking.api.InstancePortEvent;
57import org.onosproject.openstacknetworking.api.InstancePortListener;
Jian Li1064e4f2018-05-29 16:16:53 +090058import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090059import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090060import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090061import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090062import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
63import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090064import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090065import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090066import org.onosproject.openstacknode.api.OpenstackNodeEvent;
67import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090068import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090069import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090070import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090071import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090072import org.openstack4j.model.network.Port;
73import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090074import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090075import org.slf4j.Logger;
76
77import java.nio.ByteBuffer;
Jian Li60312252018-05-10 18:40:32 +090078import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090079import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090080import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070081import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090082import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090083
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070084import static java.util.concurrent.Executors.newSingleThreadExecutor;
85import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090086import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
87import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
88import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
89import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
90import 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;
Jian Li24ec59f2018-05-23 19:01:25 +090094import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
95import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Lia171a432018-06-11 11:52:11 +090096import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Jian Li7f70bb72018-07-06 23:35:30 +090097import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Li24ec59f2018-05-23 19:01:25 +090098import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Jian Liec5c32b2018-07-13 14:28:58 +090099import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900100import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +0900101import static org.slf4j.LoggerFactory.getLogger;
102
103/**
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900105 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700106@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900107public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700109 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900110
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
112 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900113 private static final String ARP_MODE = "arpMode";
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900117
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900120
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900122 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700123
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900125 protected OpenstackRouterService osRouterService;
126
daniel parkeeb8e042018-02-21 14:06:58 +0900127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900128 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129
Jian Li60312252018-05-10 18:40:32 +0900130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900131 protected InstancePortService instancePortService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li60312252018-05-10 18:40:32 +0900134 protected ClusterService clusterService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected LeadershipService leadershipService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected OpenstackFlowRuleService osFlowRuleService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900143 protected OpenstackNetworkService osNetworkService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900146 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900147
Jian Li60312252018-05-10 18:40:32 +0900148 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900149 label = "ARP processing mode, broadcast | proxy (default)")
Jian Li60312252018-05-10 18:40:32 +0900150 protected String arpMode = DEFAULT_ARP_MODE_STR;
151
152 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
153
154 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
Jian Lif96685c2018-05-21 14:14:16 +0900155 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li24ec59f2018-05-23 19:01:25 +0900156 private final InstancePortListener instPortListener = new InternalInstancePortListener();
Jian Li60312252018-05-10 18:40:32 +0900157
158 private ApplicationId appId;
159 private NodeId localNodeId;
Jian Liec5c32b2018-07-13 14:28:58 +0900160
Jian Lie1a39032018-06-19 21:49:36 +0900161 private final Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
Jian Liec5c32b2018-07-13 14:28:58 +0900162
163 // TODO: pendingInstPortIds should be purged later
Jian Lie1a39032018-06-19 21:49:36 +0900164 private final Map<String, NetFloatingIP> pendingInstPortIds = Maps.newConcurrentMap();
Jian Li60312252018-05-10 18:40:32 +0900165
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 private final ExecutorService eventExecutor = newSingleThreadExecutor(
167 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700168
Hyunsun Moon0d457362017-06-27 17:19:41 +0900169 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700170
171 @Activate
172 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900173 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
174 configService.registerProperties(getClass());
175 localNodeId = clusterService.getLocalNode().id();
Jian Lif3a28b02018-06-11 21:29:13 +0900176 osRouterService.addListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900177 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900178 instancePortService.addListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900179 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700180 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
181 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900182 }
183
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700184 @Deactivate
185 protected void deactivate() {
186 packetService.removeProcessor(packetProcessor);
Jian Lie1a39032018-06-19 21:49:36 +0900187 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900188 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900189 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900190 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900191 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900192 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900193 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700194 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900195 }
196
Jian Li60312252018-05-10 18:40:32 +0900197 @Modified
198 void modified(ComponentContext context) {
Jian Li60312252018-05-10 18:40:32 +0900199 log.info("Modified");
200 }
201
Jian Li7f70bb72018-07-06 23:35:30 +0900202 private String getArpMode() {
203 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
204 return getPropertyValue(properties, ARP_MODE);
205 }
206
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700207 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900208 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900209
Jian Li7f70bb72018-07-06 23:35:30 +0900210 if (arp.getOpCode() == ARP.OP_REQUEST && ARP_PROXY_MODE.equals(getArpMode())) {
daniel parkb5817102018-02-15 00:18:51 +0900211 if (log.isTraceEnabled()) {
212 log.trace("ARP request received from {} for {}",
213 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
214 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
215 }
216
217 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900218
219 MacAddress targetMac = null;
220
221 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
222 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
223 .findAny().orElse(null);
224
daniel park576969a2018-03-09 07:07:41 +0900225 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900226 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900227 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
228 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900229 }
230
231 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
232 targetMac = Constants.DEFAULT_GATEWAY_MAC;
233 }
234
235 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900236 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
237 return;
238 }
239
Jian Lia171a432018-06-11 11:52:11 +0900240 InstancePort instPort = instancePortService.instancePort(targetMac);
241
242 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900243
244 if (gw == null) {
245 return;
246 }
247
248 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
249 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
250 return;
251 }
252
daniel parkb5817102018-02-15 00:18:51 +0900253 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
254 targetMac, ethernet);
255
256 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900257 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900258
259 packetService.emit(new DefaultOutboundPacket(
260 context.inPacket().receivedFrom().deviceId(),
261 treatment,
262 ByteBuffer.wrap(ethReply.serialize())));
263
264 context.block();
Jian Li60312252018-05-10 18:40:32 +0900265 }
266
267 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900268 ConnectPoint cp = context.inPacket().receivedFrom();
269 PortNumber receivedPortNum = cp.port();
270 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
271 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
272
273 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
274
daniel parkb5817102018-02-15 00:18:51 +0900275 try {
Jian Li14a79f22018-06-05 03:44:22 +0900276
Jian Lid4066ea2018-06-07 01:44:45 +0900277 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
278 stream().map(r -> r.externalPeerRouterIp().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900279
Jian Lid4066ea2018-06-07 01:44:45 +0900280 // if SPA is NOT contained in existing external router IP set, we ignore it
281 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900282 return;
283 }
284
285 OpenstackNode node = osNodeService.node(cp.deviceId());
286
287 if (node == null) {
288 return;
289 }
290
291 // we only handles the ARP-Reply message received by gateway node
292 if (node.type() != GATEWAY) {
293 return;
294 }
295
296 if (receivedPortNum.equals(node.uplinkPortNum())) {
297 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900298 }
299 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900300 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900301 }
Daniel Park81a61a12016-02-26 08:24:44 +0900302 }
Daniel Park81a61a12016-02-26 08:24:44 +0900303 }
304
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700305 private class InternalPacketProcessor implements PacketProcessor {
306
307 @Override
308 public void process(PacketContext context) {
309 if (context.isHandled()) {
310 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900311 }
312
313 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
314 .stream().map(OpenstackNode::intgBridge)
315 .collect(Collectors.toSet());
316
317 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700318 // return if the packet is not from gateway nodes
319 return;
320 }
321
322 InboundPacket pkt = context.inPacket();
323 Ethernet ethernet = pkt.parsed();
324 if (ethernet != null &&
325 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900326 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700327 }
328 }
329 }
330
daniel parkeeb8e042018-02-21 14:06:58 +0900331 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900332 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900333 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900334 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900335 .flatMap(osPort -> osPort.getFixedIps().stream())
336 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900337 }
Jian Li60312252018-05-10 18:40:32 +0900338
Jian Li60312252018-05-10 18:40:32 +0900339 private void initFloatingIpMacMap() {
340 osRouterService.floatingIps().forEach(f -> {
341 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
342 Port port = osNetworkAdminService.port(f.getPortId());
343 if (port != null && port.getMacAddress() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900344 floatingIpMacMap.put(f.getFloatingIpAddress(),
345 MacAddress.valueOf(port.getMacAddress()));
Jian Li60312252018-05-10 18:40:32 +0900346 }
347 }
348 });
349 }
350
Jian Lie1a39032018-06-19 21:49:36 +0900351 private void initPendingInstPorts() {
352 osRouterService.floatingIps().forEach(f -> {
353 if (f.getPortId() != null) {
354 Port port = osNetworkAdminService.port(f.getPortId());
355 if (port != null) {
Jian Liec5c32b2018-07-13 14:28:58 +0900356 if (!Strings.isNullOrEmpty(port.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900357 pendingInstPortIds.put(f.getPortId(), f);
358 }
359 }
360 }
361 });
362 }
363
Jian Li60312252018-05-10 18:40:32 +0900364 /**
365 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900366 *
367 * @param gateway gateway node
368 * @param install flow rule installation flag
369 */
370 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900371 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900372
373 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
374 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
375 finalGws.addAll(ImmutableSet.copyOf(completedGws));
376
377 if (install) {
378 if (completedGws.contains(gateway)) {
379 if (completedGws.size() > 1) {
380 finalGws.remove(gateway);
381 osRouterService.floatingIps().forEach(fip -> {
382 if (fip.getPortId() != null) {
383 setFloatingIpArpRule(fip, finalGws, false);
384 finalGws.add(gateway);
385 }
386 });
387 }
388 osRouterService.floatingIps().forEach(fip -> {
389 if (fip.getPortId() != null) {
390 setFloatingIpArpRule(fip, finalGws, true);
391 }
392 });
393 } else {
394 log.warn("Detected node should be included in completed gateway set");
395 }
396 } else {
397 if (!completedGws.contains(gateway)) {
398 finalGws.add(gateway);
399 osRouterService.floatingIps().forEach(fip -> {
400 if (fip.getPortId() != null) {
401 setFloatingIpArpRule(fip, finalGws, false);
402 }
403 });
404 finalGws.remove(gateway);
405 if (completedGws.size() >= 1) {
406 osRouterService.floatingIps().forEach(fip -> {
407 if (fip.getPortId() != null) {
408 setFloatingIpArpRule(fip, finalGws, true);
409 }
410 });
411 }
412 } else {
413 log.warn("Detected node should NOT be included in completed gateway set");
414 }
415 }
416 }
417 }
418
419 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900420 * Installs/uninstalls ARP flow rules to the corresponding gateway by
421 * looking for compute node's device ID.
422 *
423 * @param fip floating IP
424 * @param port instance port
425 * @param gateways a collection of gateways
426 * @param install install flag
427 */
428 private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
429 InstancePort port,
430 Set<OpenstackNode> gateways,
431 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900432 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li24ec59f2018-05-23 19:01:25 +0900433
434 OpenstackNode gw = getGwByInstancePort(gateways, port);
435
436 if (gw == null) {
437 return;
438 }
439
440 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
441
442 setArpRule(fip, MacAddress.valueOf(macString), gw, install);
443 }
444 }
445
446 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900447 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900448 * Note that, those rules will be only matched ARP_REQUEST packets,
449 * used for telling gateway node the mapped MAC address of requested IP,
450 * without the helps from controller.
451 *
452 * @param fip floating IP address
Jian Li1064e4f2018-05-29 16:16:53 +0900453 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900454 * @param install flow rule installation flag
455 */
Jian Li1064e4f2018-05-29 16:16:53 +0900456 private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
457 Set<OpenstackNode> gateways,
458 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900459 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900460
461 if (fip == null) {
462 log.warn("Failed to set ARP broadcast rule for floating IP");
463 return;
464 }
465
Jian Lif3a28b02018-06-11 21:29:13 +0900466 MacAddress targetMac;
467 InstancePort instPort;
Jian Li60312252018-05-10 18:40:32 +0900468
469 if (install) {
470 if (fip.getPortId() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900471 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
472 targetMac = MacAddress.valueOf(macString);
473 floatingIpMacMap.put(fip.getFloatingIpAddress(), targetMac);
Jian Li60312252018-05-10 18:40:32 +0900474 } else {
475 log.trace("Unknown target ARP request for {}, ignore it",
476 fip.getFloatingIpAddress());
477 return;
478 }
479 } else {
Jian Lif3a28b02018-06-11 21:29:13 +0900480 targetMac = floatingIpMacMap.get(fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900481 }
482
Jian Lif3a28b02018-06-11 21:29:13 +0900483 instPort = instancePortService.instancePort(targetMac);
484
Jian Lia171a432018-06-11 11:52:11 +0900485 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900486
487 if (gw == null) {
488 return;
489 }
490
Jian Li24ec59f2018-05-23 19:01:25 +0900491 setArpRule(fip, targetMac, gw, install);
492 }
493 }
Jian Li60312252018-05-10 18:40:32 +0900494
Jian Li24ec59f2018-05-23 19:01:25 +0900495 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
496 OpenstackNode gateway, boolean install) {
497 TrafficSelector selector = DefaultTrafficSelector.builder()
498 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
499 .matchArpOp(ARP.OP_REQUEST)
500 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
501 .build();
Jian Li60312252018-05-10 18:40:32 +0900502
Jian Li24ec59f2018-05-23 19:01:25 +0900503 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
504 .setArpOp(ARP.OP_REPLY)
505 .setArpSha(targetMac)
506 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
507 .setOutput(PortNumber.IN_PORT)
508 .build();
Jian Li60312252018-05-10 18:40:32 +0900509
Jian Li24ec59f2018-05-23 19:01:25 +0900510 osFlowRuleService.setRule(
511 appId,
512 gateway.intgBridge(),
513 selector,
514 treatment,
515 PRIORITY_ARP_GATEWAY_RULE,
516 GW_COMMON_TABLE,
517 install
518 );
519
520 if (install) {
521 log.info("Install ARP Rule for Floating IP {}",
522 fip.getFloatingIpAddress());
523 } else {
524 log.info("Uninstall ARP Rule for Floating IP {}",
525 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900526 }
527 }
528
529 /**
530 * An internal router event listener, intended to install/uninstall
531 * ARP rules for forwarding packets created from floating IPs.
532 */
533 private class InternalRouterEventListener implements OpenstackRouterListener {
534
535 @Override
536 public boolean isRelevant(OpenstackRouterEvent event) {
537 // do not allow to proceed without leadership
538 NodeId leader = leadershipService.getLeader(appId.name());
539 return Objects.equals(localNodeId, leader);
540 }
541
542 @Override
543 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900544
545 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
546
Jian Li60312252018-05-10 18:40:32 +0900547 switch (event.type()) {
548 case OPENSTACK_ROUTER_CREATED:
549 eventExecutor.execute(() ->
550 // add a router with external gateway
551 setFakeGatewayArpRule(event.subject(), true)
552 );
553 break;
554 case OPENSTACK_ROUTER_REMOVED:
555 eventExecutor.execute(() ->
556 // remove a router with external gateway
557 setFakeGatewayArpRule(event.subject(), false)
558 );
559 break;
560 case OPENSTACK_ROUTER_GATEWAY_ADDED:
561 eventExecutor.execute(() ->
562 // add a gateway manually after adding a router
563 setFakeGatewayArpRule(event.externalGateway(), true)
564 );
565 break;
566 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
567 eventExecutor.execute(() ->
568 // remove a gateway from an existing router
569 setFakeGatewayArpRule(event.externalGateway(), false)
570 );
571 break;
572 case OPENSTACK_FLOATING_IP_ASSOCIATED:
573 eventExecutor.execute(() ->
574 // associate a floating IP with an existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900575 setFloatingIpArpRule(event.floatingIp(), completedGws, true)
Jian Li60312252018-05-10 18:40:32 +0900576 );
577 break;
578 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
579 eventExecutor.execute(() ->
580 // disassociate a floating IP with the existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900581 setFloatingIpArpRule(event.floatingIp(), completedGws, false)
Jian Li60312252018-05-10 18:40:32 +0900582 );
583 break;
584 case OPENSTACK_FLOATING_IP_CREATED:
585 eventExecutor.execute(() -> {
586 NetFloatingIP osFip = event.floatingIp();
587
588 // during floating IP creation, if the floating IP is
589 // associated with any port of VM, then we will set
590 // floating IP related ARP rules to gateway node
591 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900592 setFloatingIpArpRule(osFip, completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900593 }
594 });
595 break;
596 case OPENSTACK_FLOATING_IP_REMOVED:
597 eventExecutor.execute(() -> {
598 NetFloatingIP osFip = event.floatingIp();
599
600 // during floating IP deletion, if the floating IP is
601 // still associated with any port of VM, then we will
602 // remove floating IP related ARP rules from gateway node
603 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900604 setFloatingIpArpRule(event.floatingIp(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900605 }
606 });
607 break;
608 default:
609 // do nothing for the other events
610 break;
611 }
612 }
613
Jian Li4df657b2018-05-29 16:39:00 +0900614 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
615 return osNetworkAdminService.ports().stream()
616 .filter(port ->
617 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
618 .filter(port ->
619 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
620 .flatMap(port -> port.getFixedIps().stream())
621 .collect(Collectors.toSet());
622 }
623
Jian Li60312252018-05-10 18:40:32 +0900624 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900625 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900626
627 if (extGw == null) {
628 return;
629 }
630
Jian Li4df657b2018-05-29 16:39:00 +0900631 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900632
Jian Li4df657b2018-05-29 16:39:00 +0900633 ips.forEach(ip -> {
634 TrafficSelector selector = DefaultTrafficSelector.builder()
635 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
636 .matchArpOp(ARP.OP_REQUEST)
637 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
638 .build();
Jian Li60312252018-05-10 18:40:32 +0900639
Jian Li4df657b2018-05-29 16:39:00 +0900640 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
641 .setArpOp(ARP.OP_REPLY)
642 .setArpSha(MacAddress.valueOf(gatewayMac))
643 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
644 .setOutput(PortNumber.IN_PORT)
645 .build();
Jian Li60312252018-05-10 18:40:32 +0900646
Jian Li4df657b2018-05-29 16:39:00 +0900647 osNodeService.completeNodes(GATEWAY).forEach(n ->
648 osFlowRuleService.setRule(
649 appId,
650 n.intgBridge(),
651 selector,
652 treatment,
653 PRIORITY_ARP_GATEWAY_RULE,
654 GW_COMMON_TABLE,
655 install
656 )
657 );
Jian Li60312252018-05-10 18:40:32 +0900658
Jian Li4df657b2018-05-29 16:39:00 +0900659 if (install) {
660 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
661 } else {
662 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
663 }
664 });
Jian Li60312252018-05-10 18:40:32 +0900665 }
666 }
667
668 private void setFakeGatewayArpRule(Router router, boolean install) {
669 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
670 }
671 }
672
Jian Lie1a39032018-06-19 21:49:36 +0900673 private class InternalInstancePortListener implements InstancePortListener {
Jian Li60312252018-05-10 18:40:32 +0900674
675 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900676 public boolean isRelevant(InstancePortEvent event) {
677 // do not allow to proceed without leadership
678 NodeId leader = leadershipService.getLeader(appId.name());
679 return Objects.equals(localNodeId, leader);
Jian Li60312252018-05-10 18:40:32 +0900680 }
681
682 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900683 public void event(InstancePortEvent event) {
684 InstancePort instPort = event.subject();
685
686 Set<NetFloatingIP> ips = osRouterService.floatingIps();
687 NetFloatingIP fip = associatedFloatingIp(instPort, ips);
688 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
689
Jian Li60312252018-05-10 18:40:32 +0900690 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900691 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Lie1a39032018-06-19 21:49:36 +0900692
693 if (pendingInstPortIds.containsKey(instPort.portId())) {
694 Set<OpenstackNode> completedGws =
695 osNodeService.completeNodes(GATEWAY);
696 setFloatingIpArpRule(pendingInstPortIds.get(instPort.portId()),
697 completedGws, true);
698 pendingInstPortIds.remove(instPort.portId());
699 }
700
Jian Li60312252018-05-10 18:40:32 +0900701 break;
Jian Lie1a39032018-06-19 21:49:36 +0900702 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
703
704 if (gateways.size() == 1) {
705 return;
706 }
707
708 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900709 eventExecutor.execute(() ->
Jian Lie1a39032018-06-19 21:49:36 +0900710 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
Jian Liec5c32b2018-07-13 14:28:58 +0900711 gateways, true)
712 );
Jian Lie1a39032018-06-19 21:49:36 +0900713 }
714
715 break;
716 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
717
Jian Liec5c32b2018-07-13 14:28:58 +0900718 InstancePort revisedInstPort = swapStaleLocation(event.subject());
719
Jian Lie1a39032018-06-19 21:49:36 +0900720 if (gateways.size() == 1) {
721 return;
722 }
723
724 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900725 DeviceId newDeviceId = event.subject().deviceId();
726 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Lie1a39032018-06-19 21:49:36 +0900727
728 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
729 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
730
731 if (oldGw != null && oldGw.equals(newGw)) {
732 return;
733 }
734
735 eventExecutor.execute(() ->
Jian Liec5c32b2018-07-13 14:28:58 +0900736 setFloatingIpArpRuleWithPortEvent(fip,
737 revisedInstPort, gateways, false));
Jian Lie1a39032018-06-19 21:49:36 +0900738 }
739 break;
740 default:
741 break;
742 }
Jian Li60312252018-05-10 18:40:32 +0900743 }
744 }
Jian Lif96685c2018-05-21 14:14:16 +0900745
746 private class InternalNodeEventListener implements OpenstackNodeListener {
747
748 @Override
749 public boolean isRelevant(OpenstackNodeEvent event) {
750 // do not allow to proceed without leadership
751 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900752 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900753 }
754
755 @Override
756 public void event(OpenstackNodeEvent event) {
757 OpenstackNode osNode = event.subject();
758 switch (event.type()) {
759 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900760 setDefaultArpRule(osNode, true);
761 setFloatingIpArpRuleForGateway(osNode, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900762
Jian Lif3a28b02018-06-11 21:29:13 +0900763 // initialize FloatingIp to Mac map
764 initFloatingIpMacMap();
765
Jian Lie1a39032018-06-19 21:49:36 +0900766 // initialize pendingInstPorts
767 initPendingInstPorts();
768
Jian Lif96685c2018-05-21 14:14:16 +0900769 break;
770 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900771 setDefaultArpRule(osNode, false);
772 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900773 break;
774 default:
775 break;
776 }
777 }
778
779 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900780 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900781 case ARP_PROXY_MODE:
782 setDefaultArpRuleForProxyMode(osNode, install);
783 break;
784 case ARP_BROADCAST_MODE:
785 setDefaultArpRuleForBroadcastMode(osNode, install);
786 break;
787 default:
788 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900789 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900790 break;
791 }
792 }
793
794 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
795 TrafficSelector selector = DefaultTrafficSelector.builder()
796 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
797 .build();
798
799 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
800 .punt()
801 .build();
802
803 osFlowRuleService.setRule(
804 appId,
805 osNode.intgBridge(),
806 selector,
807 treatment,
808 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900809 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900810 install
811 );
812 }
813
814 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
815 // we only match ARP_REPLY in gateway node, because controller
816 // somehow need to process ARP_REPLY which is issued from
817 // external router...
818 TrafficSelector selector = DefaultTrafficSelector.builder()
819 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
820 .matchArpOp(ARP.OP_REPLY)
821 .build();
822
823 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
824 .punt()
825 .build();
826
827 osFlowRuleService.setRule(
828 appId,
829 osNode.intgBridge(),
830 selector,
831 treatment,
832 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900833 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900834 install
835 );
836 }
837 }
Daniel Park81a61a12016-02-26 08:24:44 +0900838}