blob: d751c03dca23115e64e538d97e06ae3e97927c18 [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) {
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) {
Jian Li8f64feb2018-07-24 13:20:16 +0900350 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900351 finalGws.add(gateway);
352 }
353 });
354 }
355 osRouterService.floatingIps().forEach(fip -> {
356 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900357 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900358 }
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) {
Jian Li8f64feb2018-07-24 13:20:16 +0900368 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900369 }
370 });
371 finalGws.remove(gateway);
372 if (completedGws.size() >= 1) {
373 osRouterService.floatingIps().forEach(fip -> {
374 if (fip.getPortId() != null) {
Jian Li8f64feb2018-07-24 13:20:16 +0900375 setFloatingIpArpRule(fip, fip.getPortId(), finalGws, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900376 }
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 Li8f64feb2018-07-24 13:20:16 +0900420 * @param portId port identifier
Jian Li1064e4f2018-05-29 16:16:53 +0900421 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900422 * @param install flow rule installation flag
423 */
Jian Li8f64feb2018-07-24 13:20:16 +0900424 private synchronized void setFloatingIpArpRule(NetFloatingIP fip, String portId,
Jian Li1064e4f2018-05-29 16:16:53 +0900425 Set<OpenstackNode> gateways,
426 boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900427 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900428
429 if (fip == null) {
430 log.warn("Failed to set ARP broadcast rule for floating IP");
431 return;
432 }
433
Jian Lida03ce92018-07-24 21:41:53 +0900434 if (portId == null || fip.getPortId() == null) {
435 log.trace("Unknown target ARP request for {}, ignore it",
436 fip.getFloatingIpAddress());
437 return;
438 }
439
Jian Li8f64feb2018-07-24 13:20:16 +0900440 InstancePort instPort = instancePortService.instancePort(portId);
Jian Li46b74002018-07-15 18:39:08 +0900441 MacAddress targetMac = instPort.macAddress();
Jian Lif3a28b02018-06-11 21:29:13 +0900442
Jian Lia171a432018-06-11 11:52:11 +0900443 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900444
445 if (gw == null) {
446 return;
447 }
448
Jian Li24ec59f2018-05-23 19:01:25 +0900449 setArpRule(fip, targetMac, gw, install);
450 }
451 }
Jian Li60312252018-05-10 18:40:32 +0900452
Jian Li24ec59f2018-05-23 19:01:25 +0900453 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
454 OpenstackNode gateway, boolean install) {
455 TrafficSelector selector = DefaultTrafficSelector.builder()
456 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
457 .matchArpOp(ARP.OP_REQUEST)
458 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
459 .build();
Jian Li60312252018-05-10 18:40:32 +0900460
Jian Li24ec59f2018-05-23 19:01:25 +0900461 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
462 .setArpOp(ARP.OP_REPLY)
463 .setArpSha(targetMac)
464 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
465 .setOutput(PortNumber.IN_PORT)
466 .build();
Jian Li60312252018-05-10 18:40:32 +0900467
Jian Li24ec59f2018-05-23 19:01:25 +0900468 osFlowRuleService.setRule(
469 appId,
470 gateway.intgBridge(),
471 selector,
472 treatment,
473 PRIORITY_ARP_GATEWAY_RULE,
474 GW_COMMON_TABLE,
475 install
476 );
477
478 if (install) {
479 log.info("Install ARP Rule for Floating IP {}",
480 fip.getFloatingIpAddress());
481 } else {
482 log.info("Uninstall ARP Rule for Floating IP {}",
483 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900484 }
485 }
486
487 /**
488 * An internal router event listener, intended to install/uninstall
489 * ARP rules for forwarding packets created from floating IPs.
490 */
491 private class InternalRouterEventListener implements OpenstackRouterListener {
492
493 @Override
494 public boolean isRelevant(OpenstackRouterEvent event) {
495 // do not allow to proceed without leadership
496 NodeId leader = leadershipService.getLeader(appId.name());
497 return Objects.equals(localNodeId, leader);
498 }
499
500 @Override
501 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900502
503 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
504
Jian Li60312252018-05-10 18:40:32 +0900505 switch (event.type()) {
506 case OPENSTACK_ROUTER_CREATED:
507 eventExecutor.execute(() ->
508 // add a router with external gateway
509 setFakeGatewayArpRule(event.subject(), true)
510 );
511 break;
512 case OPENSTACK_ROUTER_REMOVED:
513 eventExecutor.execute(() ->
514 // remove a router with external gateway
515 setFakeGatewayArpRule(event.subject(), false)
516 );
517 break;
518 case OPENSTACK_ROUTER_GATEWAY_ADDED:
519 eventExecutor.execute(() ->
520 // add a gateway manually after adding a router
521 setFakeGatewayArpRule(event.externalGateway(), true)
522 );
523 break;
524 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
525 eventExecutor.execute(() ->
526 // remove a gateway from an existing router
527 setFakeGatewayArpRule(event.externalGateway(), false)
528 );
529 break;
530 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Jian Lida03ce92018-07-24 21:41:53 +0900531 if (getValidPortId(event) != null) {
532 eventExecutor.execute(() -> {
533 // associate a floating IP with an existing VM
534 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
535 completedGws, true);
536 });
537 }
Jian Li60312252018-05-10 18:40:32 +0900538 break;
539 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
Jian Lida03ce92018-07-24 21:41:53 +0900540 if (getValidPortId(event) != null) {
541 eventExecutor.execute(() -> {
542 // disassociate a floating IP with an existing VM
543 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
544 completedGws, false);
545 });
546 }
Jian Li60312252018-05-10 18:40:32 +0900547 break;
548 case OPENSTACK_FLOATING_IP_CREATED:
Jian Lida03ce92018-07-24 21:41:53 +0900549 // during floating IP creation, if the floating IP is
550 // associated with any port of VM, then we will set
551 // floating IP related ARP rules to gateway node
552 if (getValidPortId(event) != null) {
553 eventExecutor.execute(() -> {
554 // associate a floating IP with an existing VM
555 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
556 completedGws, true);
557 });
558 }
Jian Li60312252018-05-10 18:40:32 +0900559 break;
560 case OPENSTACK_FLOATING_IP_REMOVED:
Jian Lida03ce92018-07-24 21:41:53 +0900561 // during floating IP deletion, if the floating IP is
562 // still associated with any port of VM, then we will
563 // remove floating IP related ARP rules from gateway node
564 if (getValidPortId(event) != null) {
565 eventExecutor.execute(() -> {
566 // associate a floating IP with an existing VM
567 setFloatingIpArpRule(event.floatingIp(), getValidPortId(event),
568 completedGws, false);
569 });
570 }
Jian Li60312252018-05-10 18:40:32 +0900571 break;
572 default:
573 // do nothing for the other events
574 break;
575 }
576 }
577
Jian Lida03ce92018-07-24 21:41:53 +0900578 private String getValidPortId(OpenstackRouterEvent event) {
579 NetFloatingIP osFip = event.floatingIp();
580 String portId = osFip.getPortId();
581
582 if (Strings.isNullOrEmpty(portId)) {
583 portId = event.portId();
584 }
585
586 if (portId != null && instancePortService.instancePort(portId) != null) {
587 return portId;
588 }
589
590 return null;
591 }
592
Jian Li4df657b2018-05-29 16:39:00 +0900593 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
594 return osNetworkAdminService.ports().stream()
595 .filter(port ->
596 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
597 .filter(port ->
598 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
599 .flatMap(port -> port.getFixedIps().stream())
600 .collect(Collectors.toSet());
601 }
602
Jian Li60312252018-05-10 18:40:32 +0900603 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900604 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li60312252018-05-10 18:40:32 +0900605
606 if (extGw == null) {
607 return;
608 }
609
Jian Li4df657b2018-05-29 16:39:00 +0900610 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900611
Jian Li4df657b2018-05-29 16:39:00 +0900612 ips.forEach(ip -> {
613 TrafficSelector selector = DefaultTrafficSelector.builder()
614 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
615 .matchArpOp(ARP.OP_REQUEST)
616 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
617 .build();
Jian Li60312252018-05-10 18:40:32 +0900618
Jian Li4df657b2018-05-29 16:39:00 +0900619 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
620 .setArpOp(ARP.OP_REPLY)
621 .setArpSha(MacAddress.valueOf(gatewayMac))
622 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
623 .setOutput(PortNumber.IN_PORT)
624 .build();
Jian Li60312252018-05-10 18:40:32 +0900625
Jian Li4df657b2018-05-29 16:39:00 +0900626 osNodeService.completeNodes(GATEWAY).forEach(n ->
627 osFlowRuleService.setRule(
628 appId,
629 n.intgBridge(),
630 selector,
631 treatment,
632 PRIORITY_ARP_GATEWAY_RULE,
633 GW_COMMON_TABLE,
634 install
635 )
636 );
Jian Li60312252018-05-10 18:40:32 +0900637
Jian Li4df657b2018-05-29 16:39:00 +0900638 if (install) {
639 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
640 } else {
641 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
642 }
643 });
Jian Li60312252018-05-10 18:40:32 +0900644 }
645 }
646
647 private void setFakeGatewayArpRule(Router router, boolean install) {
648 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
649 }
650 }
651
Jian Lie1a39032018-06-19 21:49:36 +0900652 private class InternalInstancePortListener implements InstancePortListener {
Jian Li60312252018-05-10 18:40:32 +0900653
654 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900655 public boolean isRelevant(InstancePortEvent event) {
656 // do not allow to proceed without leadership
657 NodeId leader = leadershipService.getLeader(appId.name());
658 return Objects.equals(localNodeId, leader);
Jian Li60312252018-05-10 18:40:32 +0900659 }
660
661 @Override
Jian Lie1a39032018-06-19 21:49:36 +0900662 public void event(InstancePortEvent event) {
663 InstancePort instPort = event.subject();
664
665 Set<NetFloatingIP> ips = osRouterService.floatingIps();
666 NetFloatingIP fip = associatedFloatingIp(instPort, ips);
667 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
668
Jian Li60312252018-05-10 18:40:32 +0900669 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900670 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Lie1a39032018-06-19 21:49:36 +0900671
Jian Li46b74002018-07-15 18:39:08 +0900672 osRouterService.floatingIps().stream()
673 .filter(f -> f.getPortId() != null)
674 .filter(f -> f.getPortId().equals(instPort.portId()))
Jian Li8f64feb2018-07-24 13:20:16 +0900675 .forEach(f -> setFloatingIpArpRule(f, instPort.portId(), gateways, true));
Jian Lie1a39032018-06-19 21:49:36 +0900676
Jian Li60312252018-05-10 18:40:32 +0900677 break;
Jian Lie1a39032018-06-19 21:49:36 +0900678 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
679
680 if (gateways.size() == 1) {
681 return;
682 }
683
684 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900685 eventExecutor.execute(() ->
Jian Lie1a39032018-06-19 21:49:36 +0900686 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
Jian Liec5c32b2018-07-13 14:28:58 +0900687 gateways, true)
688 );
Jian Lie1a39032018-06-19 21:49:36 +0900689 }
690
691 break;
692 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
693
Jian Liec5c32b2018-07-13 14:28:58 +0900694 InstancePort revisedInstPort = swapStaleLocation(event.subject());
695
Jian Lie1a39032018-06-19 21:49:36 +0900696 if (gateways.size() == 1) {
697 return;
698 }
699
700 if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
Jian Liec5c32b2018-07-13 14:28:58 +0900701 DeviceId newDeviceId = event.subject().deviceId();
702 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Lie1a39032018-06-19 21:49:36 +0900703
704 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
705 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
706
707 if (oldGw != null && oldGw.equals(newGw)) {
708 return;
709 }
710
711 eventExecutor.execute(() ->
Jian Liec5c32b2018-07-13 14:28:58 +0900712 setFloatingIpArpRuleWithPortEvent(fip,
713 revisedInstPort, gateways, false));
Jian Lie1a39032018-06-19 21:49:36 +0900714 }
715 break;
716 default:
717 break;
718 }
Jian Li60312252018-05-10 18:40:32 +0900719 }
720 }
Jian Lif96685c2018-05-21 14:14:16 +0900721
722 private class InternalNodeEventListener implements OpenstackNodeListener {
723
724 @Override
725 public boolean isRelevant(OpenstackNodeEvent event) {
726 // do not allow to proceed without leadership
727 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900728 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900729 }
730
731 @Override
732 public void event(OpenstackNodeEvent event) {
733 OpenstackNode osNode = event.subject();
734 switch (event.type()) {
735 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900736 setDefaultArpRule(osNode, true);
737 setFloatingIpArpRuleForGateway(osNode, true);
Jian Lif96685c2018-05-21 14:14:16 +0900738 break;
739 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900740 setDefaultArpRule(osNode, false);
741 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900742 break;
743 default:
744 break;
745 }
746 }
747
748 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900749 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900750 case ARP_PROXY_MODE:
751 setDefaultArpRuleForProxyMode(osNode, install);
752 break;
753 case ARP_BROADCAST_MODE:
754 setDefaultArpRuleForBroadcastMode(osNode, install);
755 break;
756 default:
757 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900758 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900759 break;
760 }
761 }
762
763 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
764 TrafficSelector selector = DefaultTrafficSelector.builder()
765 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
766 .build();
767
768 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
769 .punt()
770 .build();
771
772 osFlowRuleService.setRule(
773 appId,
774 osNode.intgBridge(),
775 selector,
776 treatment,
777 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900778 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900779 install
780 );
781 }
782
783 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
784 // we only match ARP_REPLY in gateway node, because controller
785 // somehow need to process ARP_REPLY which is issued from
786 // external router...
787 TrafficSelector selector = DefaultTrafficSelector.builder()
788 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
789 .matchArpOp(ARP.OP_REPLY)
790 .build();
791
792 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
793 .punt()
794 .build();
795
796 osFlowRuleService.setRule(
797 appId,
798 osNode.intgBridge(),
799 selector,
800 treatment,
801 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900802 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900803 install
804 );
805 }
806 }
Daniel Park81a61a12016-02-26 08:24:44 +0900807}