blob: 7708d062cb1dd44da457a124497626df788be5ef [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;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Jian Li60312252018-05-10 18:40:32 +090024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
Daniel Park81a61a12016-02-26 08:24:44 +090028import org.onlab.packet.ARP;
Jian Li60312252018-05-10 18:40:32 +090029import org.onlab.packet.EthType;
Daniel Park81a61a12016-02-26 08:24:44 +090030import org.onlab.packet.Ethernet;
Daniel Park81a61a12016-02-26 08:24:44 +090031import org.onlab.packet.Ip4Address;
32import org.onlab.packet.IpAddress;
33import org.onlab.packet.MacAddress;
Jian Li60312252018-05-10 18:40:32 +090034import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090035import org.onosproject.cfg.ConfigProperty;
Jian Li60312252018-05-10 18:40:32 +090036import org.onosproject.cluster.ClusterService;
37import org.onosproject.cluster.LeadershipService;
38import org.onosproject.cluster.NodeId;
39import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
Jian Li14a79f22018-06-05 03:44:22 +090041import org.onosproject.net.ConnectPoint;
Hyunsun Moon0d457362017-06-27 17:19:41 +090042import org.onosproject.net.DeviceId;
daniel parkb5817102018-02-15 00:18:51 +090043import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090044import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090045import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090046import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090047import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070049import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090050import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070051import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090052import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090053import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090054import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090055import org.onosproject.openstacknetworking.api.InstancePortEvent;
56import org.onosproject.openstacknetworking.api.InstancePortListener;
Jian Li1064e4f2018-05-29 16:16:53 +090057import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090058import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090059import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090060import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090061import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
62import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090063import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090064import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090065import org.onosproject.openstacknode.api.OpenstackNodeEvent;
66import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090068import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090069import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090070import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090071import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090072import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090073import org.slf4j.Logger;
74
75import java.nio.ByteBuffer;
Hyunsun Moon44aac662017-02-18 02:07:01 +090076import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090077import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070078import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090079import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090080
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070081import static java.util.concurrent.Executors.newSingleThreadExecutor;
82import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090083import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
84import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
85import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
86import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
87import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
88import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090089import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090090import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
Jian Li24ec59f2018-05-23 19:01:25 +090091import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
92import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Lia171a432018-06-11 11:52:11 +090093import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Jian Li7f70bb72018-07-06 23:35:30 +090094import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Li24ec59f2018-05-23 19:01:25 +090095import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Jian Liec5c32b2018-07-13 14:28:58 +090096import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Hyunsun Moon0d457362017-06-27 17:19:41 +090097import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +090098import static org.slf4j.LoggerFactory.getLogger;
99
100/**
Hyunsun Moon44aac662017-02-18 02:07:01 +0900101 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900102 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700103@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900104public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700106 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900107
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
109 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900110 private static final String ARP_MODE = "arpMode";
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900114
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900117
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900119 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700120
Hyunsun Moon44aac662017-02-18 02:07:01 +0900121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900122 protected OpenstackRouterService osRouterService;
123
daniel parkeeb8e042018-02-21 14:06:58 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900125 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900126
Jian Li60312252018-05-10 18:40:32 +0900127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900128 protected InstancePortService instancePortService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li60312252018-05-10 18:40:32 +0900131 protected ClusterService clusterService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected LeadershipService leadershipService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected OpenstackFlowRuleService osFlowRuleService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900140 protected OpenstackNetworkService osNetworkService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900143 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900144
Jian Li60312252018-05-10 18:40:32 +0900145 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900146 label = "ARP processing mode, broadcast | proxy (default)")
Jian Li60312252018-05-10 18:40:32 +0900147 protected String arpMode = DEFAULT_ARP_MODE_STR;
148
149 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
150
151 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
Jian Lif96685c2018-05-21 14:14:16 +0900152 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li24ec59f2018-05-23 19:01:25 +0900153 private final InstancePortListener instPortListener = new InternalInstancePortListener();
Jian Li60312252018-05-10 18:40:32 +0900154
155 private ApplicationId appId;
156 private NodeId localNodeId;
Jian Liec5c32b2018-07-13 14:28:58 +0900157
Hyunsun Moon44aac662017-02-18 02:07:01 +0900158 private final ExecutorService eventExecutor = newSingleThreadExecutor(
159 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700160
Hyunsun Moon0d457362017-06-27 17:19:41 +0900161 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700162
163 @Activate
164 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900165 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
166 configService.registerProperties(getClass());
167 localNodeId = clusterService.getLocalNode().id();
Jian Lif3a28b02018-06-11 21:29:13 +0900168 osRouterService.addListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900169 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900170 instancePortService.addListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900171 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700172 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
173 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900174 }
175
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700176 @Deactivate
177 protected void deactivate() {
178 packetService.removeProcessor(packetProcessor);
Jian Lie1a39032018-06-19 21:49:36 +0900179 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900180 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900181 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900182 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900183 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900185 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700186 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900187 }
188
Jian Li60312252018-05-10 18:40:32 +0900189 @Modified
190 void modified(ComponentContext context) {
Jian Li60312252018-05-10 18:40:32 +0900191 log.info("Modified");
192 }
193
Jian Li7f70bb72018-07-06 23:35:30 +0900194 private String getArpMode() {
195 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
196 return getPropertyValue(properties, ARP_MODE);
197 }
198
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700199 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900200 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900201
Jian Li7f70bb72018-07-06 23:35:30 +0900202 if (arp.getOpCode() == ARP.OP_REQUEST && ARP_PROXY_MODE.equals(getArpMode())) {
daniel parkb5817102018-02-15 00:18:51 +0900203 if (log.isTraceEnabled()) {
204 log.trace("ARP request received from {} for {}",
205 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
206 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
207 }
208
209 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900210
211 MacAddress targetMac = null;
212
213 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
214 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
215 .findAny().orElse(null);
216
daniel park576969a2018-03-09 07:07:41 +0900217 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900218 if (floatingIP != null && floatingIP.getPortId() != null) {
Daniel Park96f1e032018-08-09 13:30:57 +0900219 InstancePort instPort = instancePortService.instancePort(floatingIP.getPortId());
220 if (instPort == null) {
221 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
222 return;
223 } else {
224 targetMac = instPort.macAddress();
225 }
226
227 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
228
229 if (gw == null) {
230 return;
231 }
232
233 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
234 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
235 return;
236 }
daniel parkeeb8e042018-02-21 14:06:58 +0900237 }
238
Daniel Park96f1e032018-08-09 13:30:57 +0900239 if (isExternalGatewaySourceIp(targetIp)) {
daniel parkeeb8e042018-02-21 14:06:58 +0900240 targetMac = Constants.DEFAULT_GATEWAY_MAC;
241 }
242
243 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900244 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
245 return;
246 }
247
daniel parkb5817102018-02-15 00:18:51 +0900248 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
249 targetMac, ethernet);
250
251 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900252 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900253
254 packetService.emit(new DefaultOutboundPacket(
255 context.inPacket().receivedFrom().deviceId(),
256 treatment,
257 ByteBuffer.wrap(ethReply.serialize())));
258
259 context.block();
Jian Li60312252018-05-10 18:40:32 +0900260 }
261
262 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900263 ConnectPoint cp = context.inPacket().receivedFrom();
264 PortNumber receivedPortNum = cp.port();
265 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
266 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
267
268 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
269
daniel parkb5817102018-02-15 00:18:51 +0900270 try {
Jian Li14a79f22018-06-05 03:44:22 +0900271
Jian Lid4066ea2018-06-07 01:44:45 +0900272 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
Jian Li5e2ad4a2018-07-16 13:40:53 +0900273 stream().map(r -> r.ipAddress().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900274
Jian Lid4066ea2018-06-07 01:44:45 +0900275 // if SPA is NOT contained in existing external router IP set, we ignore it
276 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900277 return;
278 }
279
280 OpenstackNode node = osNodeService.node(cp.deviceId());
281
282 if (node == null) {
283 return;
284 }
285
286 // we only handles the ARP-Reply message received by gateway node
287 if (node.type() != GATEWAY) {
288 return;
289 }
290
291 if (receivedPortNum.equals(node.uplinkPortNum())) {
292 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900293 }
294 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900295 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900296 }
Daniel Park81a61a12016-02-26 08:24:44 +0900297 }
Daniel Park81a61a12016-02-26 08:24:44 +0900298 }
299
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700300 private class InternalPacketProcessor implements PacketProcessor {
301
302 @Override
303 public void process(PacketContext context) {
304 if (context.isHandled()) {
305 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900306 }
307
308 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
309 .stream().map(OpenstackNode::intgBridge)
310 .collect(Collectors.toSet());
311
312 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700313 // return if the packet is not from gateway nodes
314 return;
315 }
316
317 InboundPacket pkt = context.inPacket();
318 Ethernet ethernet = pkt.parsed();
319 if (ethernet != null &&
320 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900321 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700322 }
323 }
324 }
325
daniel parkeeb8e042018-02-21 14:06:58 +0900326 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900327 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900328 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900329 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900330 .flatMap(osPort -> osPort.getFixedIps().stream())
331 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900332 }
Jian Li60312252018-05-10 18:40:32 +0900333
Jian Li60312252018-05-10 18:40:32 +0900334 /**
335 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900336 *
337 * @param gateway gateway node
338 * @param install flow rule installation flag
339 */
340 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900341 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900342
343 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
344 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
345 finalGws.addAll(ImmutableSet.copyOf(completedGws));
346
347 if (install) {
348 if (completedGws.contains(gateway)) {
349 if (completedGws.size() > 1) {
350 finalGws.remove(gateway);
351 osRouterService.floatingIps().forEach(fip -> {
352 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900353 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900354 finalGws.add(gateway);
355 }
356 });
357 }
358 osRouterService.floatingIps().forEach(fip -> {
359 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900360 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900361 }
362 });
363 } else {
364 log.warn("Detected node should be included in completed gateway set");
365 }
366 } else {
367 if (!completedGws.contains(gateway)) {
368 finalGws.add(gateway);
369 osRouterService.floatingIps().forEach(fip -> {
370 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900371 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900372 }
373 });
374 finalGws.remove(gateway);
375 if (completedGws.size() >= 1) {
376 osRouterService.floatingIps().forEach(fip -> {
377 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900378 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900379 }
380 });
381 }
382 } else {
383 log.warn("Detected node should NOT be included in completed gateway set");
384 }
385 }
386 }
387 }
388
389 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900390 * Installs/uninstalls ARP flow rules to the corresponding gateway by
391 * looking for compute node's device ID.
392 *
393 * @param fip floating IP
394 * @param port instance port
395 * @param gateways a collection of gateways
396 * @param install install flag
397 */
398 private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
399 InstancePort port,
400 Set<OpenstackNode> gateways,
401 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900402 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li24ec59f2018-05-23 19:01:25 +0900403
404 OpenstackNode gw = getGwByInstancePort(gateways, port);
405
406 if (gw == null) {
407 return;
408 }
409
410 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
411
412 setArpRule(fip, MacAddress.valueOf(macString), gw, install);
413 }
414 }
415
416 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900417 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900418 * Note that, those rules will be only matched ARP_REQUEST packets,
419 * used for telling gateway node the mapped MAC address of requested IP,
420 * without the helps from controller.
421 *
422 * @param fip floating IP address
Jian Li8f64feb2018-07-24 13:20:16 +0900423 * @param portId port identifier
Jian Li1064e4f2018-05-29 16:16:53 +0900424 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900425 * @param install flow rule installation flag
426 */
Jian Li8f64feb2018-07-24 13:20:16 +0900427 private synchronized void setFloatingIpArpRule(NetFloatingIP fip, String portId,
Jian Li1064e4f2018-05-29 16:16:53 +0900428 Set<OpenstackNode> gateways,
429 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900430 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900431
432 if (fip == null) {
433 log.warn("Failed to set ARP broadcast rule for floating IP");
434 return;
435 }
436
Jian Lida03ce92018-07-24 21:41:53 +0900437 if (portId == null || fip.getPortId() == null) {
438 log.trace("Unknown target ARP request for {}, ignore it",
439 fip.getFloatingIpAddress());
440 return;
441 }
442
Jian Li8f64feb2018-07-24 13:20:16 +0900443 InstancePort instPort = instancePortService.instancePort(portId);
Jian Li46b74002018-07-15 18:39:08 +0900444 MacAddress targetMac = instPort.macAddress();
Jian Lif3a28b02018-06-11 21:29:13 +0900445
Jian Lia171a432018-06-11 11:52:11 +0900446 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900447
448 if (gw == null) {
449 return;
450 }
451
Jian Li24ec59f2018-05-23 19:01:25 +0900452 setArpRule(fip, targetMac, gw, install);
453 }
454 }
Jian Li60312252018-05-10 18:40:32 +0900455
Jian Li24ec59f2018-05-23 19:01:25 +0900456 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
457 OpenstackNode gateway, boolean install) {
458 TrafficSelector selector = DefaultTrafficSelector.builder()
459 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
460 .matchArpOp(ARP.OP_REQUEST)
461 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
462 .build();
Jian Li60312252018-05-10 18:40:32 +0900463
Jian Li24ec59f2018-05-23 19:01:25 +0900464 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
465 .setArpOp(ARP.OP_REPLY)
466 .setArpSha(targetMac)
467 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
468 .setOutput(PortNumber.IN_PORT)
469 .build();
Jian Li60312252018-05-10 18:40:32 +0900470
Jian Li24ec59f2018-05-23 19:01:25 +0900471 osFlowRuleService.setRule(
472 appId,
473 gateway.intgBridge(),
474 selector,
475 treatment,
476 PRIORITY_ARP_GATEWAY_RULE,
477 GW_COMMON_TABLE,
478 install
479 );
480
481 if (install) {
482 log.info("Install ARP Rule for Floating IP {}",
483 fip.getFloatingIpAddress());
484 } else {
485 log.info("Uninstall ARP Rule for Floating IP {}",
486 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900487 }
488 }
489
Daniel Park96f1e032018-08-09 13:30:57 +0900490 private void setFakeGatewayArpRule(Router router, boolean install) {
491 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
492 }
493
494 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
495 return osNetworkAdminService.ports().stream()
496 .filter(port ->
497 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
498 .filter(port ->
499 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
500 .flatMap(port -> port.getFixedIps().stream())
501 .collect(Collectors.toSet());
502 }
503
504 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
505 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
506
507 if (extGw == null) {
508 return;
509 }
510
511 Set<IP> ips = getExternalGatewaySnatIps(extGw);
512
513 ips.forEach(ip -> {
514 TrafficSelector selector = DefaultTrafficSelector.builder()
515 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
516 .matchArpOp(ARP.OP_REQUEST)
517 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
518 .build();
519
520 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
521 .setArpOp(ARP.OP_REPLY)
522 .setArpSha(MacAddress.valueOf(gatewayMac))
523 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
524 .setOutput(PortNumber.IN_PORT)
525 .build();
526
527 osNodeService.completeNodes(GATEWAY).forEach(n ->
528 osFlowRuleService.setRule(
529 appId,
530 n.intgBridge(),
531 selector,
532 treatment,
533 PRIORITY_ARP_GATEWAY_RULE,
534 GW_COMMON_TABLE,
535 install
536 )
537 );
538
539 if (install) {
540 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
541 } else {
542 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
543 }
544 });
545 }
546 }
547
Jian Li60312252018-05-10 18:40:32 +0900548 /**
549 * An internal router event listener, intended to install/uninstall
550 * ARP rules for forwarding packets created from floating IPs.
551 */
552 private class InternalRouterEventListener implements OpenstackRouterListener {
553
554 @Override
555 public boolean isRelevant(OpenstackRouterEvent event) {
556 // do not allow to proceed without leadership
557 NodeId leader = leadershipService.getLeader(appId.name());
558 return Objects.equals(localNodeId, leader);
559 }
560
561 @Override
562 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900563
564 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
565
Jian Li60312252018-05-10 18:40:32 +0900566 switch (event.type()) {
567 case OPENSTACK_ROUTER_CREATED:
568 eventExecutor.execute(() ->
569 // add a router with external gateway
570 setFakeGatewayArpRule(event.subject(), true)
571 );
572 break;
573 case OPENSTACK_ROUTER_REMOVED:
574 eventExecutor.execute(() ->
575 // remove a router with external gateway
576 setFakeGatewayArpRule(event.subject(), false)
577 );
578 break;
579 case OPENSTACK_ROUTER_GATEWAY_ADDED:
580 eventExecutor.execute(() ->
581 // add a gateway manually after adding a router
582 setFakeGatewayArpRule(event.externalGateway(), true)
583 );
584 break;
585 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
586 eventExecutor.execute(() ->
587 // remove a gateway from an existing router
588 setFakeGatewayArpRule(event.externalGateway(), false)
589 );
590 break;
591 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Jian Lida03ce92018-07-24 21:41:53 +0900592 if (getValidPortId(event) != null) {
593 eventExecutor.execute(() -> {
594 // associate a floating IP with an existing VM
595 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
596 completedGws, true);
597 });
598 }
Jian Li60312252018-05-10 18:40:32 +0900599 break;
600 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
Jian Lida03ce92018-07-24 21:41:53 +0900601 if (getValidPortId(event) != null) {
602 eventExecutor.execute(() -> {
603 // disassociate a floating IP with an existing VM
604 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
605 completedGws, false);
606 });
607 }
Jian Li60312252018-05-10 18:40:32 +0900608 break;
609 case OPENSTACK_FLOATING_IP_CREATED:
Jian Lida03ce92018-07-24 21:41:53 +0900610 // during floating IP creation, if the floating IP is
611 // associated with any port of VM, then we will set
612 // floating IP related ARP rules to gateway node
613 if (getValidPortId(event) != null) {
614 eventExecutor.execute(() -> {
615 // associate a floating IP with an existing VM
616 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
617 completedGws, true);
618 });
619 }
Jian Li60312252018-05-10 18:40:32 +0900620 break;
621 case OPENSTACK_FLOATING_IP_REMOVED:
Jian Lida03ce92018-07-24 21:41:53 +0900622 // during floating IP deletion, if the floating IP is
623 // still associated with any port of VM, then we will
624 // remove floating IP related ARP rules from gateway node
625 if (getValidPortId(event) != null) {
626 eventExecutor.execute(() -> {
627 // associate a floating IP with an existing VM
628 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
629 completedGws, false);
630 });
631 }
Jian Li60312252018-05-10 18:40:32 +0900632 break;
633 default:
634 // do nothing for the other events
635 break;
636 }
637 }
638
Jian Lida03ce92018-07-24 21:41:53 +0900639 private String getValidPortId(OpenstackRouterEvent event) {
640 NetFloatingIP osFip = event.floatingIp();
641 String portId = osFip.getPortId();
642
643 if (Strings.isNullOrEmpty(portId)) {
644 portId = event.portId();
645 }
646
647 if (portId != null && instancePortService.instancePort(portId) != null) {
648 return portId;
649 }
650
651 return null;
652 }
Jian Li60312252018-05-10 18:40:32 +0900653 }
654
Jian Lie1a39032018-06-19 21:49:36 +0900655 private class InternalInstancePortListener implements InstancePortListener {
Jian Li60312252018-05-10 18:40:32 +0900656
657 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900658 public boolean isRelevant(InstancePortEvent event) {
659 // do not allow to proceed without leadership
660 NodeId leader = leadershipService.getLeader(appId.name());
661 return Objects.equals(localNodeId, leader);
Jian Li60312252018-05-10 18:40:32 +0900662 }
663
664 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900665 public void event(InstancePortEvent event) {
666 InstancePort instPort = event.subject();
667
668 Set<NetFloatingIP> ips = osRouterService.floatingIps();
669 NetFloatingIP fip = associatedFloatingIp(instPort, ips);
670 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
671
Jian Li60312252018-05-10 18:40:32 +0900672 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900673 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Lie1a39032018-06-19 21:49:36 +0900674
Jian Li46b74002018-07-15 18:39:08 +0900675 osRouterService.floatingIps().stream()
676 .filter(f -> f.getPortId() != null)
677 .filter(f -> f.getPortId().equals(instPort.portId()))
Jian Li8f64feb2018-07-24 13:20:16 +0900678 .forEach(f -> setFloatingIpArpRule(f, instPort.portId(), gateways, true));
Jian Lie1a39032018-06-19 21:49:36 +0900679
Jian Li60312252018-05-10 18:40:32 +0900680 break;
Jian Lie1a39032018-06-19 21:49:36 +0900681 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
682
683 if (gateways.size() == 1) {
684 return;
685 }
686
687 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900688 eventExecutor.execute(() ->
Jian Lie1a39032018-06-19 21:49:36 +0900689 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
Jian Liec5c32b2018-07-13 14:28:58 +0900690 gateways, true)
691 );
Jian Lie1a39032018-06-19 21:49:36 +0900692 }
693
694 break;
695 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
696
Jian Liec5c32b2018-07-13 14:28:58 +0900697 InstancePort revisedInstPort = swapStaleLocation(event.subject());
698
Jian Lie1a39032018-06-19 21:49:36 +0900699 if (gateways.size() == 1) {
700 return;
701 }
702
703 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900704 DeviceId newDeviceId = event.subject().deviceId();
705 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Lie1a39032018-06-19 21:49:36 +0900706
707 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
708 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
709
710 if (oldGw != null && oldGw.equals(newGw)) {
711 return;
712 }
713
714 eventExecutor.execute(() ->
Jian Liec5c32b2018-07-13 14:28:58 +0900715 setFloatingIpArpRuleWithPortEvent(fip,
716 revisedInstPort, gateways, false));
Jian Lie1a39032018-06-19 21:49:36 +0900717 }
718 break;
719 default:
720 break;
721 }
Jian Li60312252018-05-10 18:40:32 +0900722 }
723 }
Jian Lif96685c2018-05-21 14:14:16 +0900724
725 private class InternalNodeEventListener implements OpenstackNodeListener {
726
727 @Override
728 public boolean isRelevant(OpenstackNodeEvent event) {
729 // do not allow to proceed without leadership
730 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900731 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900732 }
733
734 @Override
735 public void event(OpenstackNodeEvent event) {
736 OpenstackNode osNode = event.subject();
737 switch (event.type()) {
738 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900739 setDefaultArpRule(osNode, true);
740 setFloatingIpArpRuleForGateway(osNode, true);
Jian Lif96685c2018-05-21 14:14:16 +0900741 break;
742 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900743 setDefaultArpRule(osNode, false);
744 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900745 break;
746 default:
747 break;
748 }
749 }
750
751 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900752 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900753 case ARP_PROXY_MODE:
754 setDefaultArpRuleForProxyMode(osNode, install);
755 break;
756 case ARP_BROADCAST_MODE:
757 setDefaultArpRuleForBroadcastMode(osNode, install);
758 break;
759 default:
760 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900761 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900762 break;
763 }
764 }
765
766 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
767 TrafficSelector selector = DefaultTrafficSelector.builder()
768 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
769 .build();
770
771 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
772 .punt()
773 .build();
774
775 osFlowRuleService.setRule(
776 appId,
777 osNode.intgBridge(),
778 selector,
779 treatment,
780 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900781 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900782 install
783 );
784 }
785
786 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
787 // we only match ARP_REPLY in gateway node, because controller
788 // somehow need to process ARP_REPLY which is issued from
789 // external router...
790 TrafficSelector selector = DefaultTrafficSelector.builder()
791 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
792 .matchArpOp(ARP.OP_REPLY)
793 .build();
794
795 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
796 .punt()
797 .build();
798
799 osFlowRuleService.setRule(
800 appId,
801 osNode.intgBridge(),
802 selector,
803 treatment,
804 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900805 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900806 install
807 );
Daniel Park96f1e032018-08-09 13:30:57 +0900808
809 osRouterService.routers().stream()
810 .filter(router -> router.getExternalGatewayInfo() != null)
811 .forEach(router -> setFakeGatewayArpRule(router, install));
Jian Lif96685c2018-05-21 14:14:16 +0900812 }
813 }
Daniel Park81a61a12016-02-26 08:24:44 +0900814}