blob: 9e47a0c72f4a6097d8d01143c0135d58d84cdbb9 [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 Li1064e4f2018-05-29 16:16:53 +090018import com.google.common.collect.ImmutableSet;
Jian Li1064e4f2018-05-29 16:16:53 +090019import com.google.common.collect.Sets;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Jian Li60312252018-05-10 18:40:32 +090023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Daniel Park81a61a12016-02-26 08:24:44 +090027import org.onlab.packet.ARP;
Jian Li60312252018-05-10 18:40:32 +090028import org.onlab.packet.EthType;
Daniel Park81a61a12016-02-26 08:24:44 +090029import org.onlab.packet.Ethernet;
Daniel Park81a61a12016-02-26 08:24:44 +090030import org.onlab.packet.Ip4Address;
31import org.onlab.packet.IpAddress;
32import org.onlab.packet.MacAddress;
Jian Li60312252018-05-10 18:40:32 +090033import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090034import org.onosproject.cfg.ConfigProperty;
Jian Li60312252018-05-10 18:40:32 +090035import org.onosproject.cluster.ClusterService;
36import org.onosproject.cluster.LeadershipService;
37import org.onosproject.cluster.NodeId;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Jian Li14a79f22018-06-05 03:44:22 +090040import org.onosproject.net.ConnectPoint;
Hyunsun Moon0d457362017-06-27 17:19:41 +090041import org.onosproject.net.DeviceId;
daniel parkb5817102018-02-15 00:18:51 +090042import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090043import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090044import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090045import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090046import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070048import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090049import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070050import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090051import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090052import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090053import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090054import org.onosproject.openstacknetworking.api.InstancePortEvent;
55import org.onosproject.openstacknetworking.api.InstancePortListener;
Jian Li1064e4f2018-05-29 16:16:53 +090056import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090057import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090058import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090059import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090060import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
61import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090062import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090063import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090064import org.onosproject.openstacknode.api.OpenstackNodeEvent;
65import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090066import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090067import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090068import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090069import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090070import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090071import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090072import org.slf4j.Logger;
73
74import java.nio.ByteBuffer;
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
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900116
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900118 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700119
Hyunsun Moon44aac662017-02-18 02:07:01 +0900120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900121 protected OpenstackRouterService osRouterService;
122
daniel parkeeb8e042018-02-21 14:06:58 +0900123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900124 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125
Jian Li60312252018-05-10 18:40:32 +0900126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900127 protected InstancePortService instancePortService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li60312252018-05-10 18:40:32 +0900130 protected ClusterService clusterService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected LeadershipService leadershipService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected OpenstackFlowRuleService osFlowRuleService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900139 protected OpenstackNetworkService osNetworkService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900142 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900143
Jian Li60312252018-05-10 18:40:32 +0900144 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900145 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) {
Jian Li60312252018-05-10 18:40:32 +0900218 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
219 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900220 }
221
222 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
223 targetMac = Constants.DEFAULT_GATEWAY_MAC;
224 }
225
226 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900227 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
228 return;
229 }
230
Jian Lia171a432018-06-11 11:52:11 +0900231 InstancePort instPort = instancePortService.instancePort(targetMac);
232
233 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900234
235 if (gw == null) {
236 return;
237 }
238
239 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
240 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
241 return;
242 }
243
daniel parkb5817102018-02-15 00:18:51 +0900244 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
245 targetMac, ethernet);
246
247 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900248 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900249
250 packetService.emit(new DefaultOutboundPacket(
251 context.inPacket().receivedFrom().deviceId(),
252 treatment,
253 ByteBuffer.wrap(ethReply.serialize())));
254
255 context.block();
Jian Li60312252018-05-10 18:40:32 +0900256 }
257
258 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900259 ConnectPoint cp = context.inPacket().receivedFrom();
260 PortNumber receivedPortNum = cp.port();
261 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
262 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
263
264 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
265
daniel parkb5817102018-02-15 00:18:51 +0900266 try {
Jian Li14a79f22018-06-05 03:44:22 +0900267
Jian Lid4066ea2018-06-07 01:44:45 +0900268 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
Jian Li5e2ad4a2018-07-16 13:40:53 +0900269 stream().map(r -> r.ipAddress().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900270
Jian Lid4066ea2018-06-07 01:44:45 +0900271 // if SPA is NOT contained in existing external router IP set, we ignore it
272 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900273 return;
274 }
275
276 OpenstackNode node = osNodeService.node(cp.deviceId());
277
278 if (node == null) {
279 return;
280 }
281
282 // we only handles the ARP-Reply message received by gateway node
283 if (node.type() != GATEWAY) {
284 return;
285 }
286
287 if (receivedPortNum.equals(node.uplinkPortNum())) {
288 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900289 }
290 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900291 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900292 }
Daniel Park81a61a12016-02-26 08:24:44 +0900293 }
Daniel Park81a61a12016-02-26 08:24:44 +0900294 }
295
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700296 private class InternalPacketProcessor implements PacketProcessor {
297
298 @Override
299 public void process(PacketContext context) {
300 if (context.isHandled()) {
301 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900302 }
303
304 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
305 .stream().map(OpenstackNode::intgBridge)
306 .collect(Collectors.toSet());
307
308 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700309 // return if the packet is not from gateway nodes
310 return;
311 }
312
313 InboundPacket pkt = context.inPacket();
314 Ethernet ethernet = pkt.parsed();
315 if (ethernet != null &&
316 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900317 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700318 }
319 }
320 }
321
daniel parkeeb8e042018-02-21 14:06:58 +0900322 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900323 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900324 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900325 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900326 .flatMap(osPort -> osPort.getFixedIps().stream())
327 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900328 }
Jian Li60312252018-05-10 18:40:32 +0900329
Jian Li60312252018-05-10 18:40:32 +0900330 /**
331 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900332 *
333 * @param gateway gateway node
334 * @param install flow rule installation flag
335 */
336 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900337 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900338
339 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
340 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
341 finalGws.addAll(ImmutableSet.copyOf(completedGws));
342
343 if (install) {
344 if (completedGws.contains(gateway)) {
345 if (completedGws.size() > 1) {
346 finalGws.remove(gateway);
347 osRouterService.floatingIps().forEach(fip -> {
348 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900349 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900350 finalGws.add(gateway);
351 }
352 });
353 }
354 osRouterService.floatingIps().forEach(fip -> {
355 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900356 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900357 }
358 });
359 } else {
360 log.warn("Detected node should be included in completed gateway set");
361 }
362 } else {
363 if (!completedGws.contains(gateway)) {
364 finalGws.add(gateway);
365 osRouterService.floatingIps().forEach(fip -> {
366 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900367 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900368 }
369 });
370 finalGws.remove(gateway);
371 if (completedGws.size() >= 1) {
372 osRouterService.floatingIps().forEach(fip -> {
373 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900374 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900375 }
376 });
377 }
378 } else {
379 log.warn("Detected node should NOT be included in completed gateway set");
380 }
381 }
382 }
383 }
384
385 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900386 * Installs/uninstalls ARP flow rules to the corresponding gateway by
387 * looking for compute node's device ID.
388 *
389 * @param fip floating IP
390 * @param port instance port
391 * @param gateways a collection of gateways
392 * @param install install flag
393 */
394 private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
395 InstancePort port,
396 Set<OpenstackNode> gateways,
397 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900398 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li24ec59f2018-05-23 19:01:25 +0900399
400 OpenstackNode gw = getGwByInstancePort(gateways, port);
401
402 if (gw == null) {
403 return;
404 }
405
406 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
407
408 setArpRule(fip, MacAddress.valueOf(macString), gw, install);
409 }
410 }
411
412 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900413 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900414 * Note that, those rules will be only matched ARP_REQUEST packets,
415 * used for telling gateway node the mapped MAC address of requested IP,
416 * without the helps from controller.
417 *
418 * @param fip floating IP address
Jian Li8f64feb2018-07-24 13:20:16 +0900419 * @param portId port identifier
Jian Li1064e4f2018-05-29 16:16:53 +0900420 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900421 * @param install flow rule installation flag
422 */
Jian Li8f64feb2018-07-24 13:20:16 +0900423 private synchronized void setFloatingIpArpRule(NetFloatingIP fip, String portId,
Jian Li1064e4f2018-05-29 16:16:53 +0900424 Set<OpenstackNode> gateways,
425 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900426 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900427
428 if (fip == null) {
429 log.warn("Failed to set ARP broadcast rule for floating IP");
430 return;
431 }
432
Jian Li8f64feb2018-07-24 13:20:16 +0900433 InstancePort instPort = instancePortService.instancePort(portId);
Jian Li46b74002018-07-15 18:39:08 +0900434 MacAddress targetMac = instPort.macAddress();
Jian Lif3a28b02018-06-11 21:29:13 +0900435
Jian Lia171a432018-06-11 11:52:11 +0900436 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900437
438 if (gw == null) {
439 return;
440 }
441
Jian Li24ec59f2018-05-23 19:01:25 +0900442 setArpRule(fip, targetMac, gw, install);
443 }
444 }
Jian Li60312252018-05-10 18:40:32 +0900445
Jian Li24ec59f2018-05-23 19:01:25 +0900446 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
447 OpenstackNode gateway, boolean install) {
448 TrafficSelector selector = DefaultTrafficSelector.builder()
449 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
450 .matchArpOp(ARP.OP_REQUEST)
451 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
452 .build();
Jian Li60312252018-05-10 18:40:32 +0900453
Jian Li24ec59f2018-05-23 19:01:25 +0900454 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
455 .setArpOp(ARP.OP_REPLY)
456 .setArpSha(targetMac)
457 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
458 .setOutput(PortNumber.IN_PORT)
459 .build();
Jian Li60312252018-05-10 18:40:32 +0900460
Jian Li24ec59f2018-05-23 19:01:25 +0900461 osFlowRuleService.setRule(
462 appId,
463 gateway.intgBridge(),
464 selector,
465 treatment,
466 PRIORITY_ARP_GATEWAY_RULE,
467 GW_COMMON_TABLE,
468 install
469 );
470
471 if (install) {
472 log.info("Install ARP Rule for Floating IP {}",
473 fip.getFloatingIpAddress());
474 } else {
475 log.info("Uninstall ARP Rule for Floating IP {}",
476 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900477 }
478 }
479
480 /**
481 * An internal router event listener, intended to install/uninstall
482 * ARP rules for forwarding packets created from floating IPs.
483 */
484 private class InternalRouterEventListener implements OpenstackRouterListener {
485
486 @Override
487 public boolean isRelevant(OpenstackRouterEvent event) {
488 // do not allow to proceed without leadership
489 NodeId leader = leadershipService.getLeader(appId.name());
490 return Objects.equals(localNodeId, leader);
491 }
492
493 @Override
494 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900495
496 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
497
Jian Li60312252018-05-10 18:40:32 +0900498 switch (event.type()) {
499 case OPENSTACK_ROUTER_CREATED:
500 eventExecutor.execute(() ->
501 // add a router with external gateway
502 setFakeGatewayArpRule(event.subject(), true)
503 );
504 break;
505 case OPENSTACK_ROUTER_REMOVED:
506 eventExecutor.execute(() ->
507 // remove a router with external gateway
508 setFakeGatewayArpRule(event.subject(), false)
509 );
510 break;
511 case OPENSTACK_ROUTER_GATEWAY_ADDED:
512 eventExecutor.execute(() ->
513 // add a gateway manually after adding a router
514 setFakeGatewayArpRule(event.externalGateway(), true)
515 );
516 break;
517 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
518 eventExecutor.execute(() ->
519 // remove a gateway from an existing router
520 setFakeGatewayArpRule(event.externalGateway(), false)
521 );
522 break;
523 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Jian Lic2403592018-07-18 12:56:45 +0900524 eventExecutor.execute(() -> {
525 NetFloatingIP osFip = event.floatingIp();
526
Jian Li8f64feb2018-07-24 13:20:16 +0900527 // associate a floating IP with an existing VM
528 setFloatingIpArpRule(event.floatingIp(), event.portId(), completedGws, true);
Jian Lic2403592018-07-18 12:56:45 +0900529 });
Jian Li60312252018-05-10 18:40:32 +0900530 break;
531 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
Jian Lic2403592018-07-18 12:56:45 +0900532 eventExecutor.execute(() -> {
533 NetFloatingIP osFip = event.floatingIp();
534
Jian Li8f64feb2018-07-24 13:20:16 +0900535 // associate a floating IP with an existing VM
536 setFloatingIpArpRule(event.floatingIp(), event.portId(), completedGws, false);
Jian Lic2403592018-07-18 12:56:45 +0900537 });
Jian Li60312252018-05-10 18:40:32 +0900538 break;
539 case OPENSTACK_FLOATING_IP_CREATED:
540 eventExecutor.execute(() -> {
541 NetFloatingIP osFip = event.floatingIp();
542
543 // during floating IP creation, if the floating IP is
544 // associated with any port of VM, then we will set
545 // floating IP related ARP rules to gateway node
Jian Li8f64feb2018-07-24 13:20:16 +0900546 setFloatingIpArpRule(osFip, event.portId(), completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900547 });
548 break;
549 case OPENSTACK_FLOATING_IP_REMOVED:
550 eventExecutor.execute(() -> {
551 NetFloatingIP osFip = event.floatingIp();
552
553 // during floating IP deletion, if the floating IP is
554 // still associated with any port of VM, then we will
555 // remove floating IP related ARP rules from gateway node
Jian Li8f64feb2018-07-24 13:20:16 +0900556 setFloatingIpArpRule(event.floatingIp(), event.portId(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900557 });
558 break;
559 default:
560 // do nothing for the other events
561 break;
562 }
563 }
564
Jian Li4df657b2018-05-29 16:39:00 +0900565 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
566 return osNetworkAdminService.ports().stream()
567 .filter(port ->
568 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
569 .filter(port ->
570 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
571 .flatMap(port -> port.getFixedIps().stream())
572 .collect(Collectors.toSet());
573 }
574
Jian Li60312252018-05-10 18:40:32 +0900575 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900576 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900577
578 if (extGw == null) {
579 return;
580 }
581
Jian Li4df657b2018-05-29 16:39:00 +0900582 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900583
Jian Li4df657b2018-05-29 16:39:00 +0900584 ips.forEach(ip -> {
585 TrafficSelector selector = DefaultTrafficSelector.builder()
586 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
587 .matchArpOp(ARP.OP_REQUEST)
588 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
589 .build();
Jian Li60312252018-05-10 18:40:32 +0900590
Jian Li4df657b2018-05-29 16:39:00 +0900591 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
592 .setArpOp(ARP.OP_REPLY)
593 .setArpSha(MacAddress.valueOf(gatewayMac))
594 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
595 .setOutput(PortNumber.IN_PORT)
596 .build();
Jian Li60312252018-05-10 18:40:32 +0900597
Jian Li4df657b2018-05-29 16:39:00 +0900598 osNodeService.completeNodes(GATEWAY).forEach(n ->
599 osFlowRuleService.setRule(
600 appId,
601 n.intgBridge(),
602 selector,
603 treatment,
604 PRIORITY_ARP_GATEWAY_RULE,
605 GW_COMMON_TABLE,
606 install
607 )
608 );
Jian Li60312252018-05-10 18:40:32 +0900609
Jian Li4df657b2018-05-29 16:39:00 +0900610 if (install) {
611 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
612 } else {
613 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
614 }
615 });
Jian Li60312252018-05-10 18:40:32 +0900616 }
617 }
618
619 private void setFakeGatewayArpRule(Router router, boolean install) {
620 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
621 }
622 }
623
Jian Lie1a39032018-06-19 21:49:36 +0900624 private class InternalInstancePortListener implements InstancePortListener {
Jian Li60312252018-05-10 18:40:32 +0900625
626 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900627 public boolean isRelevant(InstancePortEvent event) {
628 // do not allow to proceed without leadership
629 NodeId leader = leadershipService.getLeader(appId.name());
630 return Objects.equals(localNodeId, leader);
Jian Li60312252018-05-10 18:40:32 +0900631 }
632
633 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900634 public void event(InstancePortEvent event) {
635 InstancePort instPort = event.subject();
636
637 Set<NetFloatingIP> ips = osRouterService.floatingIps();
638 NetFloatingIP fip = associatedFloatingIp(instPort, ips);
639 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
640
Jian Li60312252018-05-10 18:40:32 +0900641 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900642 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Lie1a39032018-06-19 21:49:36 +0900643
Jian Li46b74002018-07-15 18:39:08 +0900644 osRouterService.floatingIps().stream()
645 .filter(f -> f.getPortId() != null)
646 .filter(f -> f.getPortId().equals(instPort.portId()))
Jian Li8f64feb2018-07-24 13:20:16 +0900647 .forEach(f -> setFloatingIpArpRule(f, instPort.portId(), gateways, true));
Jian Lie1a39032018-06-19 21:49:36 +0900648
Jian Li60312252018-05-10 18:40:32 +0900649 break;
Jian Lie1a39032018-06-19 21:49:36 +0900650 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
651
652 if (gateways.size() == 1) {
653 return;
654 }
655
656 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900657 eventExecutor.execute(() ->
Jian Lie1a39032018-06-19 21:49:36 +0900658 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
Jian Liec5c32b2018-07-13 14:28:58 +0900659 gateways, true)
660 );
Jian Lie1a39032018-06-19 21:49:36 +0900661 }
662
663 break;
664 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
665
Jian Liec5c32b2018-07-13 14:28:58 +0900666 InstancePort revisedInstPort = swapStaleLocation(event.subject());
667
Jian Lie1a39032018-06-19 21:49:36 +0900668 if (gateways.size() == 1) {
669 return;
670 }
671
672 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900673 DeviceId newDeviceId = event.subject().deviceId();
674 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Lie1a39032018-06-19 21:49:36 +0900675
676 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
677 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
678
679 if (oldGw != null && oldGw.equals(newGw)) {
680 return;
681 }
682
683 eventExecutor.execute(() ->
Jian Liec5c32b2018-07-13 14:28:58 +0900684 setFloatingIpArpRuleWithPortEvent(fip,
685 revisedInstPort, gateways, false));
Jian Lie1a39032018-06-19 21:49:36 +0900686 }
687 break;
688 default:
689 break;
690 }
Jian Li60312252018-05-10 18:40:32 +0900691 }
692 }
Jian Lif96685c2018-05-21 14:14:16 +0900693
694 private class InternalNodeEventListener implements OpenstackNodeListener {
695
696 @Override
697 public boolean isRelevant(OpenstackNodeEvent event) {
698 // do not allow to proceed without leadership
699 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900700 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900701 }
702
703 @Override
704 public void event(OpenstackNodeEvent event) {
705 OpenstackNode osNode = event.subject();
706 switch (event.type()) {
707 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900708 setDefaultArpRule(osNode, true);
709 setFloatingIpArpRuleForGateway(osNode, true);
Jian Lif96685c2018-05-21 14:14:16 +0900710 break;
711 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900712 setDefaultArpRule(osNode, false);
713 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900714 break;
715 default:
716 break;
717 }
718 }
719
720 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900721 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900722 case ARP_PROXY_MODE:
723 setDefaultArpRuleForProxyMode(osNode, install);
724 break;
725 case ARP_BROADCAST_MODE:
726 setDefaultArpRuleForBroadcastMode(osNode, install);
727 break;
728 default:
729 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900730 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900731 break;
732 }
733 }
734
735 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
736 TrafficSelector selector = DefaultTrafficSelector.builder()
737 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
738 .build();
739
740 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
741 .punt()
742 .build();
743
744 osFlowRuleService.setRule(
745 appId,
746 osNode.intgBridge(),
747 selector,
748 treatment,
749 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900750 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900751 install
752 );
753 }
754
755 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
756 // we only match ARP_REPLY in gateway node, because controller
757 // somehow need to process ARP_REPLY which is issued from
758 // external router...
759 TrafficSelector selector = DefaultTrafficSelector.builder()
760 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
761 .matchArpOp(ARP.OP_REPLY)
762 .build();
763
764 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
765 .punt()
766 .build();
767
768 osFlowRuleService.setRule(
769 appId,
770 osNode.intgBridge(),
771 selector,
772 treatment,
773 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900774 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900775 install
776 );
777 }
778 }
Daniel Park81a61a12016-02-26 08:24:44 +0900779}