blob: dc83d98c2deb8f2344a547eeadc8bf7a82b2ed84 [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 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) {
Jian Li60312252018-05-10 18:40:32 +0900219 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
220 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900221 }
222
223 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
224 targetMac = Constants.DEFAULT_GATEWAY_MAC;
225 }
226
227 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900228 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
229 return;
230 }
231
Jian Lia171a432018-06-11 11:52:11 +0900232 InstancePort instPort = instancePortService.instancePort(targetMac);
233
234 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900235
236 if (gw == null) {
237 return;
238 }
239
240 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
241 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
242 return;
243 }
244
daniel parkb5817102018-02-15 00:18:51 +0900245 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
246 targetMac, ethernet);
247
248 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900249 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900250
251 packetService.emit(new DefaultOutboundPacket(
252 context.inPacket().receivedFrom().deviceId(),
253 treatment,
254 ByteBuffer.wrap(ethReply.serialize())));
255
256 context.block();
Jian Li60312252018-05-10 18:40:32 +0900257 }
258
259 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900260 ConnectPoint cp = context.inPacket().receivedFrom();
261 PortNumber receivedPortNum = cp.port();
262 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
263 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
264
265 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
266
daniel parkb5817102018-02-15 00:18:51 +0900267 try {
Jian Li14a79f22018-06-05 03:44:22 +0900268
Jian Lid4066ea2018-06-07 01:44:45 +0900269 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
Jian Li5e2ad4a2018-07-16 13:40:53 +0900270 stream().map(r -> r.ipAddress().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900271
Jian Lid4066ea2018-06-07 01:44:45 +0900272 // if SPA is NOT contained in existing external router IP set, we ignore it
273 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900274 return;
275 }
276
277 OpenstackNode node = osNodeService.node(cp.deviceId());
278
279 if (node == null) {
280 return;
281 }
282
283 // we only handles the ARP-Reply message received by gateway node
284 if (node.type() != GATEWAY) {
285 return;
286 }
287
288 if (receivedPortNum.equals(node.uplinkPortNum())) {
289 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900290 }
291 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900292 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900293 }
Daniel Park81a61a12016-02-26 08:24:44 +0900294 }
Daniel Park81a61a12016-02-26 08:24:44 +0900295 }
296
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700297 private class InternalPacketProcessor implements PacketProcessor {
298
299 @Override
300 public void process(PacketContext context) {
301 if (context.isHandled()) {
302 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900303 }
304
305 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
306 .stream().map(OpenstackNode::intgBridge)
307 .collect(Collectors.toSet());
308
309 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700310 // return if the packet is not from gateway nodes
311 return;
312 }
313
314 InboundPacket pkt = context.inPacket();
315 Ethernet ethernet = pkt.parsed();
316 if (ethernet != null &&
317 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900318 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700319 }
320 }
321 }
322
daniel parkeeb8e042018-02-21 14:06:58 +0900323 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900324 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900325 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900326 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900327 .flatMap(osPort -> osPort.getFixedIps().stream())
328 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900329 }
Jian Li60312252018-05-10 18:40:32 +0900330
Jian Li60312252018-05-10 18:40:32 +0900331 /**
332 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900333 *
334 * @param gateway gateway node
335 * @param install flow rule installation flag
336 */
337 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900338 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900339
340 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
341 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
342 finalGws.addAll(ImmutableSet.copyOf(completedGws));
343
344 if (install) {
345 if (completedGws.contains(gateway)) {
346 if (completedGws.size() > 1) {
347 finalGws.remove(gateway);
348 osRouterService.floatingIps().forEach(fip -> {
349 if (fip.getPortId() != null) {
350 setFloatingIpArpRule(fip, finalGws, false);
351 finalGws.add(gateway);
352 }
353 });
354 }
355 osRouterService.floatingIps().forEach(fip -> {
356 if (fip.getPortId() != null) {
357 setFloatingIpArpRule(fip, finalGws, true);
358 }
359 });
360 } else {
361 log.warn("Detected node should be included in completed gateway set");
362 }
363 } else {
364 if (!completedGws.contains(gateway)) {
365 finalGws.add(gateway);
366 osRouterService.floatingIps().forEach(fip -> {
367 if (fip.getPortId() != null) {
368 setFloatingIpArpRule(fip, finalGws, false);
369 }
370 });
371 finalGws.remove(gateway);
372 if (completedGws.size() >= 1) {
373 osRouterService.floatingIps().forEach(fip -> {
374 if (fip.getPortId() != null) {
375 setFloatingIpArpRule(fip, finalGws, true);
376 }
377 });
378 }
379 } else {
380 log.warn("Detected node should NOT be included in completed gateway set");
381 }
382 }
383 }
384 }
385
386 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900387 * Installs/uninstalls ARP flow rules to the corresponding gateway by
388 * looking for compute node's device ID.
389 *
390 * @param fip floating IP
391 * @param port instance port
392 * @param gateways a collection of gateways
393 * @param install install flag
394 */
395 private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
396 InstancePort port,
397 Set<OpenstackNode> gateways,
398 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900399 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li24ec59f2018-05-23 19:01:25 +0900400
401 OpenstackNode gw = getGwByInstancePort(gateways, port);
402
403 if (gw == null) {
404 return;
405 }
406
407 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
408
409 setArpRule(fip, MacAddress.valueOf(macString), gw, install);
410 }
411 }
412
413 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900414 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900415 * Note that, those rules will be only matched ARP_REQUEST packets,
416 * used for telling gateway node the mapped MAC address of requested IP,
417 * without the helps from controller.
418 *
419 * @param fip floating IP address
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 Li1064e4f2018-05-29 16:16:53 +0900423 private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
424 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 Li46b74002018-07-15 18:39:08 +0900433 if (fip.getPortId() == null) {
434 log.trace("Unknown target ARP request for {}, ignore it",
435 fip.getFloatingIpAddress());
436 return;
Jian Li60312252018-05-10 18:40:32 +0900437 }
438
Jian Li46b74002018-07-15 18:39:08 +0900439 InstancePort instPort = instancePortService.instancePort(fip.getPortId());
440 MacAddress targetMac = instPort.macAddress();
Jian Lif3a28b02018-06-11 21:29:13 +0900441
Jian Lia171a432018-06-11 11:52:11 +0900442 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900443
444 if (gw == null) {
445 return;
446 }
447
Jian Li24ec59f2018-05-23 19:01:25 +0900448 setArpRule(fip, targetMac, gw, install);
449 }
450 }
Jian Li60312252018-05-10 18:40:32 +0900451
Jian Li24ec59f2018-05-23 19:01:25 +0900452 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
453 OpenstackNode gateway, boolean install) {
454 TrafficSelector selector = DefaultTrafficSelector.builder()
455 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
456 .matchArpOp(ARP.OP_REQUEST)
457 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
458 .build();
Jian Li60312252018-05-10 18:40:32 +0900459
Jian Li24ec59f2018-05-23 19:01:25 +0900460 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
461 .setArpOp(ARP.OP_REPLY)
462 .setArpSha(targetMac)
463 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
464 .setOutput(PortNumber.IN_PORT)
465 .build();
Jian Li60312252018-05-10 18:40:32 +0900466
Jian Li24ec59f2018-05-23 19:01:25 +0900467 osFlowRuleService.setRule(
468 appId,
469 gateway.intgBridge(),
470 selector,
471 treatment,
472 PRIORITY_ARP_GATEWAY_RULE,
473 GW_COMMON_TABLE,
474 install
475 );
476
477 if (install) {
478 log.info("Install ARP Rule for Floating IP {}",
479 fip.getFloatingIpAddress());
480 } else {
481 log.info("Uninstall ARP Rule for Floating IP {}",
482 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900483 }
484 }
485
486 /**
487 * An internal router event listener, intended to install/uninstall
488 * ARP rules for forwarding packets created from floating IPs.
489 */
490 private class InternalRouterEventListener implements OpenstackRouterListener {
491
492 @Override
493 public boolean isRelevant(OpenstackRouterEvent event) {
494 // do not allow to proceed without leadership
495 NodeId leader = leadershipService.getLeader(appId.name());
496 return Objects.equals(localNodeId, leader);
497 }
498
499 @Override
500 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900501
502 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
503
Jian Li60312252018-05-10 18:40:32 +0900504 switch (event.type()) {
505 case OPENSTACK_ROUTER_CREATED:
506 eventExecutor.execute(() ->
507 // add a router with external gateway
508 setFakeGatewayArpRule(event.subject(), true)
509 );
510 break;
511 case OPENSTACK_ROUTER_REMOVED:
512 eventExecutor.execute(() ->
513 // remove a router with external gateway
514 setFakeGatewayArpRule(event.subject(), false)
515 );
516 break;
517 case OPENSTACK_ROUTER_GATEWAY_ADDED:
518 eventExecutor.execute(() ->
519 // add a gateway manually after adding a router
520 setFakeGatewayArpRule(event.externalGateway(), true)
521 );
522 break;
523 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
524 eventExecutor.execute(() ->
525 // remove a gateway from an existing router
526 setFakeGatewayArpRule(event.externalGateway(), false)
527 );
528 break;
529 case OPENSTACK_FLOATING_IP_ASSOCIATED:
530 eventExecutor.execute(() ->
531 // associate a floating IP with an existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900532 setFloatingIpArpRule(event.floatingIp(), completedGws, true)
Jian Li60312252018-05-10 18:40:32 +0900533 );
534 break;
535 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
536 eventExecutor.execute(() ->
537 // disassociate a floating IP with the existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900538 setFloatingIpArpRule(event.floatingIp(), completedGws, false)
Jian Li60312252018-05-10 18:40:32 +0900539 );
540 break;
541 case OPENSTACK_FLOATING_IP_CREATED:
542 eventExecutor.execute(() -> {
543 NetFloatingIP osFip = event.floatingIp();
544
545 // during floating IP creation, if the floating IP is
546 // associated with any port of VM, then we will set
547 // floating IP related ARP rules to gateway node
548 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900549 setFloatingIpArpRule(osFip, completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900550 }
551 });
552 break;
553 case OPENSTACK_FLOATING_IP_REMOVED:
554 eventExecutor.execute(() -> {
555 NetFloatingIP osFip = event.floatingIp();
556
557 // during floating IP deletion, if the floating IP is
558 // still associated with any port of VM, then we will
559 // remove floating IP related ARP rules from gateway node
560 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900561 setFloatingIpArpRule(event.floatingIp(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900562 }
563 });
564 break;
565 default:
566 // do nothing for the other events
567 break;
568 }
569 }
570
Jian Li4df657b2018-05-29 16:39:00 +0900571 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
572 return osNetworkAdminService.ports().stream()
573 .filter(port ->
574 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
575 .filter(port ->
576 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
577 .flatMap(port -> port.getFixedIps().stream())
578 .collect(Collectors.toSet());
579 }
580
Jian Li60312252018-05-10 18:40:32 +0900581 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900582 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900583
584 if (extGw == null) {
585 return;
586 }
587
Jian Li4df657b2018-05-29 16:39:00 +0900588 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900589
Jian Li4df657b2018-05-29 16:39:00 +0900590 ips.forEach(ip -> {
591 TrafficSelector selector = DefaultTrafficSelector.builder()
592 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
593 .matchArpOp(ARP.OP_REQUEST)
594 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
595 .build();
Jian Li60312252018-05-10 18:40:32 +0900596
Jian Li4df657b2018-05-29 16:39:00 +0900597 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
598 .setArpOp(ARP.OP_REPLY)
599 .setArpSha(MacAddress.valueOf(gatewayMac))
600 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
601 .setOutput(PortNumber.IN_PORT)
602 .build();
Jian Li60312252018-05-10 18:40:32 +0900603
Jian Li4df657b2018-05-29 16:39:00 +0900604 osNodeService.completeNodes(GATEWAY).forEach(n ->
605 osFlowRuleService.setRule(
606 appId,
607 n.intgBridge(),
608 selector,
609 treatment,
610 PRIORITY_ARP_GATEWAY_RULE,
611 GW_COMMON_TABLE,
612 install
613 )
614 );
Jian Li60312252018-05-10 18:40:32 +0900615
Jian Li4df657b2018-05-29 16:39:00 +0900616 if (install) {
617 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
618 } else {
619 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
620 }
621 });
Jian Li60312252018-05-10 18:40:32 +0900622 }
623 }
624
625 private void setFakeGatewayArpRule(Router router, boolean install) {
626 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
627 }
628 }
629
Jian Lie1a39032018-06-19 21:49:36 +0900630 private class InternalInstancePortListener implements InstancePortListener {
Jian Li60312252018-05-10 18:40:32 +0900631
632 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900633 public boolean isRelevant(InstancePortEvent event) {
634 // do not allow to proceed without leadership
635 NodeId leader = leadershipService.getLeader(appId.name());
636 return Objects.equals(localNodeId, leader);
Jian Li60312252018-05-10 18:40:32 +0900637 }
638
639 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900640 public void event(InstancePortEvent event) {
641 InstancePort instPort = event.subject();
642
643 Set<NetFloatingIP> ips = osRouterService.floatingIps();
644 NetFloatingIP fip = associatedFloatingIp(instPort, ips);
645 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
646
Jian Li60312252018-05-10 18:40:32 +0900647 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900648 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Lie1a39032018-06-19 21:49:36 +0900649
Jian Li46b74002018-07-15 18:39:08 +0900650 osRouterService.floatingIps().stream()
651 .filter(f -> f.getPortId() != null)
652 .filter(f -> f.getPortId().equals(instPort.portId()))
653 .forEach(f -> setFloatingIpArpRule(f, gateways, true));
Jian Lie1a39032018-06-19 21:49:36 +0900654
Jian Li60312252018-05-10 18:40:32 +0900655 break;
Jian Lie1a39032018-06-19 21:49:36 +0900656 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
657
658 if (gateways.size() == 1) {
659 return;
660 }
661
662 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900663 eventExecutor.execute(() ->
Jian Lie1a39032018-06-19 21:49:36 +0900664 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
Jian Liec5c32b2018-07-13 14:28:58 +0900665 gateways, true)
666 );
Jian Lie1a39032018-06-19 21:49:36 +0900667 }
668
669 break;
670 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
671
Jian Liec5c32b2018-07-13 14:28:58 +0900672 InstancePort revisedInstPort = swapStaleLocation(event.subject());
673
Jian Lie1a39032018-06-19 21:49:36 +0900674 if (gateways.size() == 1) {
675 return;
676 }
677
678 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900679 DeviceId newDeviceId = event.subject().deviceId();
680 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Lie1a39032018-06-19 21:49:36 +0900681
682 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
683 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
684
685 if (oldGw != null && oldGw.equals(newGw)) {
686 return;
687 }
688
689 eventExecutor.execute(() ->
Jian Liec5c32b2018-07-13 14:28:58 +0900690 setFloatingIpArpRuleWithPortEvent(fip,
691 revisedInstPort, gateways, false));
Jian Lie1a39032018-06-19 21:49:36 +0900692 }
693 break;
694 default:
695 break;
696 }
Jian Li60312252018-05-10 18:40:32 +0900697 }
698 }
Jian Lif96685c2018-05-21 14:14:16 +0900699
700 private class InternalNodeEventListener implements OpenstackNodeListener {
701
702 @Override
703 public boolean isRelevant(OpenstackNodeEvent event) {
704 // do not allow to proceed without leadership
705 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900706 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900707 }
708
709 @Override
710 public void event(OpenstackNodeEvent event) {
711 OpenstackNode osNode = event.subject();
712 switch (event.type()) {
713 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900714 setDefaultArpRule(osNode, true);
715 setFloatingIpArpRuleForGateway(osNode, true);
Jian Lif96685c2018-05-21 14:14:16 +0900716 break;
717 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900718 setDefaultArpRule(osNode, false);
719 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900720 break;
721 default:
722 break;
723 }
724 }
725
726 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900727 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900728 case ARP_PROXY_MODE:
729 setDefaultArpRuleForProxyMode(osNode, install);
730 break;
731 case ARP_BROADCAST_MODE:
732 setDefaultArpRuleForBroadcastMode(osNode, install);
733 break;
734 default:
735 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900736 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900737 break;
738 }
739 }
740
741 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
742 TrafficSelector selector = DefaultTrafficSelector.builder()
743 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
744 .build();
745
746 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
747 .punt()
748 .build();
749
750 osFlowRuleService.setRule(
751 appId,
752 osNode.intgBridge(),
753 selector,
754 treatment,
755 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900756 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900757 install
758 );
759 }
760
761 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
762 // we only match ARP_REPLY in gateway node, because controller
763 // somehow need to process ARP_REPLY which is issued from
764 // external router...
765 TrafficSelector selector = DefaultTrafficSelector.builder()
766 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
767 .matchArpOp(ARP.OP_REPLY)
768 .build();
769
770 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
771 .punt()
772 .build();
773
774 osFlowRuleService.setRule(
775 appId,
776 osNode.intgBridge(),
777 selector,
778 treatment,
779 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900780 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900781 install
782 );
783 }
784 }
Daniel Park81a61a12016-02-26 08:24:44 +0900785}