blob: 8c3c56dc36092b47f13d2e278e068af7b3b48637 [file] [log] [blame]
Daniel Park81a61a12016-02-26 08:24:44 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Daniel Park81a61a12016-02-26 08:24:44 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Hyunsun Moon05400872017-02-07 17:11:25 +090016package org.onosproject.openstacknetworking.impl;
Daniel Park81a61a12016-02-26 08:24:44 +090017
Jian Li60312252018-05-10 18:40:32 +090018import com.google.common.base.Strings;
Jian Li1064e4f2018-05-29 16:16:53 +090019import com.google.common.collect.ImmutableSet;
Jian Li60312252018-05-10 18:40:32 +090020import com.google.common.collect.Maps;
Jian Li1064e4f2018-05-29 16:16:53 +090021import com.google.common.collect.Sets;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Jian Li60312252018-05-10 18:40:32 +090025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Daniel Park81a61a12016-02-26 08:24:44 +090029import org.onlab.packet.ARP;
Jian Li60312252018-05-10 18:40:32 +090030import org.onlab.packet.EthType;
Daniel Park81a61a12016-02-26 08:24:44 +090031import org.onlab.packet.Ethernet;
Daniel Park81a61a12016-02-26 08:24:44 +090032import org.onlab.packet.Ip4Address;
33import org.onlab.packet.IpAddress;
34import org.onlab.packet.MacAddress;
Jian Li60312252018-05-10 18:40:32 +090035import org.onlab.util.Tools;
36import org.onosproject.cfg.ComponentConfigService;
37import org.onosproject.cluster.ClusterService;
38import org.onosproject.cluster.LeadershipService;
39import org.onosproject.cluster.NodeId;
40import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
Jian Li14a79f22018-06-05 03:44:22 +090042import org.onosproject.net.ConnectPoint;
Hyunsun Moon0d457362017-06-27 17:19:41 +090043import org.onosproject.net.DeviceId;
Jian Li60312252018-05-10 18:40:32 +090044import org.onosproject.net.Host;
daniel parkb5817102018-02-15 00:18:51 +090045import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090046import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090047import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090048import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090049import org.onosproject.net.flow.TrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090050import org.onosproject.net.host.HostEvent;
51import org.onosproject.net.host.HostListener;
52import org.onosproject.net.host.HostService;
Daniel Park81a61a12016-02-26 08:24:44 +090053import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070054import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090055import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070056import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090057import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090058import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090059import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090060import org.onosproject.openstacknetworking.api.InstancePortEvent;
61import org.onosproject.openstacknetworking.api.InstancePortListener;
Jian Li1064e4f2018-05-29 16:16:53 +090062import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090063import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090064import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090065import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090066import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
67import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090068import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090069import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090070import org.onosproject.openstacknode.api.OpenstackNodeEvent;
71import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090072import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090073import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090074import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090075import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090076import org.openstack4j.model.network.Port;
77import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090078import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090079import org.slf4j.Logger;
80
81import java.nio.ByteBuffer;
Jian Li60312252018-05-10 18:40:32 +090082import java.util.Dictionary;
83import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090084import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090085import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070086import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090087import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090088
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070089import static java.util.concurrent.Executors.newSingleThreadExecutor;
90import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090091import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
92import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
93import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
94import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
95import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
96import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090097import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090098import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
99import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
100import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li24ec59f2018-05-23 19:01:25 +0900101import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
102import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Lia171a432018-06-11 11:52:11 +0900103import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +0900104import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900105import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +0900106import static org.slf4j.LoggerFactory.getLogger;
107
108/**
Hyunsun Moon44aac662017-02-18 02:07:01 +0900109 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900110 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700111@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900112public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700114 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900115
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
117 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900118 private static final String ARP_MODE = "arpMode";
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900125
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900127 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700128
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900130 protected OpenstackRouterService osRouterService;
131
daniel parkeeb8e042018-02-21 14:06:58 +0900132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900133 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900134
Jian Li60312252018-05-10 18:40:32 +0900135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900136 protected InstancePortService instancePortService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li60312252018-05-10 18:40:32 +0900139 protected ClusterService clusterService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected LeadershipService leadershipService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected OpenstackFlowRuleService osFlowRuleService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900148 protected OpenstackNetworkService osNetworkService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900151 protected HostService hostService;
Jian Li60312252018-05-10 18:40:32 +0900152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900154 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900155
156 // TODO: need to find a way to unify aprMode and gatewayMac variables with
157 // that in SwitchingArpHandler
158 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Jian Li1478f292018-05-28 17:10:59 +0900159 label = "ARP processing mode, broadcast (default) | proxy ")
Jian Li60312252018-05-10 18:40:32 +0900160 protected String arpMode = DEFAULT_ARP_MODE_STR;
161
162 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
163
164 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
165 private final HostListener hostListener = new InternalHostListener();
Jian Lif96685c2018-05-21 14:14:16 +0900166 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li24ec59f2018-05-23 19:01:25 +0900167 private final InstancePortListener instPortListener = new InternalInstancePortListener();
Jian Li60312252018-05-10 18:40:32 +0900168
169 private ApplicationId appId;
170 private NodeId localNodeId;
Jian Lif3a28b02018-06-11 21:29:13 +0900171 private Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
172 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Jian Li24ec59f2018-05-23 19:01:25 +0900173 private Map<String, DeviceId> migrationPool = Maps.newConcurrentMap();
Jian Li60312252018-05-10 18:40:32 +0900174
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175 private final ExecutorService eventExecutor = newSingleThreadExecutor(
176 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700177
Hyunsun Moon0d457362017-06-27 17:19:41 +0900178 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700179
180 @Activate
181 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900182 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
183 configService.registerProperties(getClass());
184 localNodeId = clusterService.getLocalNode().id();
Jian Li60312252018-05-10 18:40:32 +0900185 hostService.addListener(hostListener);
Jian Lif3a28b02018-06-11 21:29:13 +0900186 osRouterService.addListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900187 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900188 instancePortService.addListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900189 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700190 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
191 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900192 }
193
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700194 @Deactivate
195 protected void deactivate() {
196 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900197 hostService.removeListener(hostListener);
198 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900199 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900200 instancePortService.removeListener(instPortListener);
Jian Li60312252018-05-10 18:40:32 +0900201 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900202 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900203 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700204 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900205 }
206
Jian Li60312252018-05-10 18:40:32 +0900207 // TODO: need to find a way to unify aprMode and gatewayMac variables with
208 // that in SwitchingArpHandler
209 @Modified
210 void modified(ComponentContext context) {
211 Dictionary<?, ?> properties = context.getProperties();
212 String updateArpMode;
213
214 updateArpMode = Tools.get(properties, ARP_MODE);
215 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
216 arpMode = updateArpMode;
217 }
218
219 log.info("Modified");
220 }
221
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700222 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900223 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900224
225 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900226 if (log.isTraceEnabled()) {
227 log.trace("ARP request received from {} for {}",
228 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
229 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
230 }
231
232 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900233
234 MacAddress targetMac = null;
235
236 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
237 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
238 .findAny().orElse(null);
239
daniel park576969a2018-03-09 07:07:41 +0900240 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900241 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900242 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
243 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900244 }
245
246 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
247 targetMac = Constants.DEFAULT_GATEWAY_MAC;
248 }
249
250 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900251 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
252 return;
253 }
254
Jian Lia171a432018-06-11 11:52:11 +0900255 InstancePort instPort = instancePortService.instancePort(targetMac);
256
257 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900258
259 if (gw == null) {
260 return;
261 }
262
263 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
264 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
265 return;
266 }
267
daniel parkb5817102018-02-15 00:18:51 +0900268 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
269 targetMac, ethernet);
270
271 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900272 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900273
274 packetService.emit(new DefaultOutboundPacket(
275 context.inPacket().receivedFrom().deviceId(),
276 treatment,
277 ByteBuffer.wrap(ethReply.serialize())));
278
279 context.block();
Jian Li60312252018-05-10 18:40:32 +0900280 }
281
282 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900283 ConnectPoint cp = context.inPacket().receivedFrom();
284 PortNumber receivedPortNum = cp.port();
285 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
286 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
287
288 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
289
daniel parkb5817102018-02-15 00:18:51 +0900290 try {
Jian Li14a79f22018-06-05 03:44:22 +0900291
Jian Lid4066ea2018-06-07 01:44:45 +0900292 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
293 stream().map(r -> r.externalPeerRouterIp().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900294
Jian Lid4066ea2018-06-07 01:44:45 +0900295 // if SPA is NOT contained in existing external router IP set, we ignore it
296 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900297 return;
298 }
299
300 OpenstackNode node = osNodeService.node(cp.deviceId());
301
302 if (node == null) {
303 return;
304 }
305
306 // we only handles the ARP-Reply message received by gateway node
307 if (node.type() != GATEWAY) {
308 return;
309 }
310
311 if (receivedPortNum.equals(node.uplinkPortNum())) {
312 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900313 }
314 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900315 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900316 }
Daniel Park81a61a12016-02-26 08:24:44 +0900317 }
Daniel Park81a61a12016-02-26 08:24:44 +0900318 }
319
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700320 private class InternalPacketProcessor implements PacketProcessor {
321
322 @Override
323 public void process(PacketContext context) {
324 if (context.isHandled()) {
325 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900326 }
327
328 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
329 .stream().map(OpenstackNode::intgBridge)
330 .collect(Collectors.toSet());
331
332 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700333 // return if the packet is not from gateway nodes
334 return;
335 }
336
337 InboundPacket pkt = context.inPacket();
338 Ethernet ethernet = pkt.parsed();
339 if (ethernet != null &&
340 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900341 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700342 }
343 }
344 }
345
daniel parkeeb8e042018-02-21 14:06:58 +0900346 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900347 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900348 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900349 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900350 .flatMap(osPort -> osPort.getFixedIps().stream())
351 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900352 }
Jian Li60312252018-05-10 18:40:32 +0900353
Jian Li60312252018-05-10 18:40:32 +0900354 private void initFloatingIpMacMap() {
355 osRouterService.floatingIps().forEach(f -> {
356 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
357 Port port = osNetworkAdminService.port(f.getPortId());
358 if (port != null && port.getMacAddress() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900359 floatingIpMacMap.put(f.getFloatingIpAddress(),
360 MacAddress.valueOf(port.getMacAddress()));
Jian Li60312252018-05-10 18:40:32 +0900361 }
362 }
363 });
364 }
365
366 /**
367 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900368 *
369 * @param gateway gateway node
370 * @param install flow rule installation flag
371 */
372 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
373 if (arpMode.equals(ARP_BROADCAST_MODE)) {
374
375 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
376 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
377 finalGws.addAll(ImmutableSet.copyOf(completedGws));
378
379 if (install) {
380 if (completedGws.contains(gateway)) {
381 if (completedGws.size() > 1) {
382 finalGws.remove(gateway);
383 osRouterService.floatingIps().forEach(fip -> {
384 if (fip.getPortId() != null) {
385 setFloatingIpArpRule(fip, finalGws, false);
386 finalGws.add(gateway);
387 }
388 });
389 }
390 osRouterService.floatingIps().forEach(fip -> {
391 if (fip.getPortId() != null) {
392 setFloatingIpArpRule(fip, finalGws, true);
393 }
394 });
395 } else {
396 log.warn("Detected node should be included in completed gateway set");
397 }
398 } else {
399 if (!completedGws.contains(gateway)) {
400 finalGws.add(gateway);
401 osRouterService.floatingIps().forEach(fip -> {
402 if (fip.getPortId() != null) {
403 setFloatingIpArpRule(fip, finalGws, false);
404 }
405 });
406 finalGws.remove(gateway);
407 if (completedGws.size() >= 1) {
408 osRouterService.floatingIps().forEach(fip -> {
409 if (fip.getPortId() != null) {
410 setFloatingIpArpRule(fip, finalGws, true);
411 }
412 });
413 }
414 } else {
415 log.warn("Detected node should NOT be included in completed gateway set");
416 }
417 }
418 }
419 }
420
421 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900422 * Installs/uninstalls ARP flow rules to the corresponding gateway by
423 * looking for compute node's device ID.
424 *
425 * @param fip floating IP
426 * @param port instance port
427 * @param gateways a collection of gateways
428 * @param install install flag
429 */
430 private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
431 InstancePort port,
432 Set<OpenstackNode> gateways,
433 boolean install) {
434 if (arpMode.equals(ARP_BROADCAST_MODE)) {
435
436 OpenstackNode gw = getGwByInstancePort(gateways, port);
437
438 if (gw == null) {
439 return;
440 }
441
442 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
443
444 setArpRule(fip, MacAddress.valueOf(macString), gw, install);
445 }
446 }
447
448 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900449 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900450 * Note that, those rules will be only matched ARP_REQUEST packets,
451 * used for telling gateway node the mapped MAC address of requested IP,
452 * without the helps from controller.
453 *
454 * @param fip floating IP address
Jian Li1064e4f2018-05-29 16:16:53 +0900455 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900456 * @param install flow rule installation flag
457 */
Jian Li1064e4f2018-05-29 16:16:53 +0900458 private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
459 Set<OpenstackNode> gateways,
460 boolean install) {
Jian Li60312252018-05-10 18:40:32 +0900461 if (arpMode.equals(ARP_BROADCAST_MODE)) {
462
463 if (fip == null) {
464 log.warn("Failed to set ARP broadcast rule for floating IP");
465 return;
466 }
467
Jian Lif3a28b02018-06-11 21:29:13 +0900468 MacAddress targetMac;
469 InstancePort instPort;
Jian Li60312252018-05-10 18:40:32 +0900470
471 if (install) {
472 if (fip.getPortId() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900473 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
474 targetMac = MacAddress.valueOf(macString);
475 floatingIpMacMap.put(fip.getFloatingIpAddress(), targetMac);
Jian Li60312252018-05-10 18:40:32 +0900476 } else {
477 log.trace("Unknown target ARP request for {}, ignore it",
478 fip.getFloatingIpAddress());
479 return;
480 }
481 } else {
Jian Lif3a28b02018-06-11 21:29:13 +0900482 targetMac = floatingIpMacMap.get(fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900483 }
484
Jian Lif3a28b02018-06-11 21:29:13 +0900485 instPort = instancePortService.instancePort(targetMac);
486
487 // in VM purge case, we will have null instance port
488 if (instPort == null) {
489 instPort = removedPorts.get(targetMac);
490 removedPorts.remove(targetMac);
491 }
Jian Li60312252018-05-10 18:40:32 +0900492
Jian Lia171a432018-06-11 11:52:11 +0900493 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900494
495 if (gw == null) {
496 return;
497 }
498
Jian Li24ec59f2018-05-23 19:01:25 +0900499 setArpRule(fip, targetMac, gw, install);
500 }
501 }
Jian Li60312252018-05-10 18:40:32 +0900502
Jian Li24ec59f2018-05-23 19:01:25 +0900503 private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
504 OpenstackNode gateway, boolean install) {
505 TrafficSelector selector = DefaultTrafficSelector.builder()
506 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
507 .matchArpOp(ARP.OP_REQUEST)
508 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
509 .build();
Jian Li60312252018-05-10 18:40:32 +0900510
Jian Li24ec59f2018-05-23 19:01:25 +0900511 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
512 .setArpOp(ARP.OP_REPLY)
513 .setArpSha(targetMac)
514 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
515 .setOutput(PortNumber.IN_PORT)
516 .build();
Jian Li60312252018-05-10 18:40:32 +0900517
Jian Li24ec59f2018-05-23 19:01:25 +0900518 osFlowRuleService.setRule(
519 appId,
520 gateway.intgBridge(),
521 selector,
522 treatment,
523 PRIORITY_ARP_GATEWAY_RULE,
524 GW_COMMON_TABLE,
525 install
526 );
527
528 if (install) {
529 log.info("Install ARP Rule for Floating IP {}",
530 fip.getFloatingIpAddress());
531 } else {
532 log.info("Uninstall ARP Rule for Floating IP {}",
533 fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900534 }
535 }
536
537 /**
538 * An internal router event listener, intended to install/uninstall
539 * ARP rules for forwarding packets created from floating IPs.
540 */
541 private class InternalRouterEventListener implements OpenstackRouterListener {
542
543 @Override
544 public boolean isRelevant(OpenstackRouterEvent event) {
545 // do not allow to proceed without leadership
546 NodeId leader = leadershipService.getLeader(appId.name());
547 return Objects.equals(localNodeId, leader);
548 }
549
550 @Override
551 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900552
553 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
554
Jian Li60312252018-05-10 18:40:32 +0900555 switch (event.type()) {
556 case OPENSTACK_ROUTER_CREATED:
557 eventExecutor.execute(() ->
558 // add a router with external gateway
559 setFakeGatewayArpRule(event.subject(), true)
560 );
561 break;
562 case OPENSTACK_ROUTER_REMOVED:
563 eventExecutor.execute(() ->
564 // remove a router with external gateway
565 setFakeGatewayArpRule(event.subject(), false)
566 );
567 break;
568 case OPENSTACK_ROUTER_GATEWAY_ADDED:
569 eventExecutor.execute(() ->
570 // add a gateway manually after adding a router
571 setFakeGatewayArpRule(event.externalGateway(), true)
572 );
573 break;
574 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
575 eventExecutor.execute(() ->
576 // remove a gateway from an existing router
577 setFakeGatewayArpRule(event.externalGateway(), false)
578 );
579 break;
580 case OPENSTACK_FLOATING_IP_ASSOCIATED:
581 eventExecutor.execute(() ->
582 // associate a floating IP with an existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900583 setFloatingIpArpRule(event.floatingIp(), completedGws, true)
Jian Li60312252018-05-10 18:40:32 +0900584 );
585 break;
586 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
587 eventExecutor.execute(() ->
588 // disassociate a floating IP with the existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900589 setFloatingIpArpRule(event.floatingIp(), completedGws, false)
Jian Li60312252018-05-10 18:40:32 +0900590 );
591 break;
592 case OPENSTACK_FLOATING_IP_CREATED:
593 eventExecutor.execute(() -> {
594 NetFloatingIP osFip = event.floatingIp();
595
596 // during floating IP creation, if the floating IP is
597 // associated with any port of VM, then we will set
598 // floating IP related ARP rules to gateway node
599 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900600 setFloatingIpArpRule(osFip, completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900601 }
602 });
603 break;
604 case OPENSTACK_FLOATING_IP_REMOVED:
605 eventExecutor.execute(() -> {
606 NetFloatingIP osFip = event.floatingIp();
607
608 // during floating IP deletion, if the floating IP is
609 // still associated with any port of VM, then we will
610 // remove floating IP related ARP rules from gateway node
611 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900612 setFloatingIpArpRule(event.floatingIp(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900613 }
614 });
615 break;
616 default:
617 // do nothing for the other events
618 break;
619 }
620 }
621
Jian Li4df657b2018-05-29 16:39:00 +0900622 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
623 return osNetworkAdminService.ports().stream()
624 .filter(port ->
625 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
626 .filter(port ->
627 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
628 .flatMap(port -> port.getFixedIps().stream())
629 .collect(Collectors.toSet());
630 }
631
Jian Li60312252018-05-10 18:40:32 +0900632 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
633 if (arpMode.equals(ARP_BROADCAST_MODE)) {
634
635 if (extGw == null) {
636 return;
637 }
638
Jian Li4df657b2018-05-29 16:39:00 +0900639 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900640
Jian Li4df657b2018-05-29 16:39:00 +0900641 ips.forEach(ip -> {
642 TrafficSelector selector = DefaultTrafficSelector.builder()
643 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
644 .matchArpOp(ARP.OP_REQUEST)
645 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
646 .build();
Jian Li60312252018-05-10 18:40:32 +0900647
Jian Li4df657b2018-05-29 16:39:00 +0900648 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
649 .setArpOp(ARP.OP_REPLY)
650 .setArpSha(MacAddress.valueOf(gatewayMac))
651 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
652 .setOutput(PortNumber.IN_PORT)
653 .build();
Jian Li60312252018-05-10 18:40:32 +0900654
Jian Li4df657b2018-05-29 16:39:00 +0900655 osNodeService.completeNodes(GATEWAY).forEach(n ->
656 osFlowRuleService.setRule(
657 appId,
658 n.intgBridge(),
659 selector,
660 treatment,
661 PRIORITY_ARP_GATEWAY_RULE,
662 GW_COMMON_TABLE,
663 install
664 )
665 );
Jian Li60312252018-05-10 18:40:32 +0900666
Jian Li4df657b2018-05-29 16:39:00 +0900667 if (install) {
668 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
669 } else {
670 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
671 }
672 });
Jian Li60312252018-05-10 18:40:32 +0900673 }
674 }
675
676 private void setFakeGatewayArpRule(Router router, boolean install) {
677 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
678 }
679 }
680
681 /**
682 * An internal host event listener, intended to uninstall
683 * ARP rules during host removal. Note that this is only valid when users
684 * remove host without disassociating floating IP with existing VM.
685 */
686 private class InternalHostListener implements HostListener {
687
688 @Override
689 public boolean isRelevant(HostEvent event) {
690 Host host = event.subject();
691 if (!isValidHost(host)) {
692 log.debug("Invalid host detected, ignore it {}", host);
693 return false;
694 }
695 return true;
696 }
697
698 @Override
699 public void event(HostEvent event) {
700 InstancePort instPort = HostBasedInstancePort.of(event.subject());
701 switch (event.type()) {
702 case HOST_REMOVED:
Jian Lif3a28b02018-06-11 21:29:13 +0900703 storeTempInstPort(instPort);
Jian Li60312252018-05-10 18:40:32 +0900704 break;
705 case HOST_UPDATED:
706 case HOST_ADDED:
707 default:
708 break;
709 }
710 }
711
Jian Lif3a28b02018-06-11 21:29:13 +0900712 private void storeTempInstPort(InstancePort port) {
Jian Li60312252018-05-10 18:40:32 +0900713 Set<NetFloatingIP> ips = osRouterService.floatingIps();
714 for (NetFloatingIP fip : ips) {
715 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
716 continue;
717 }
718 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
719 continue;
720 }
721 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
Jian Lif3a28b02018-06-11 21:29:13 +0900722 removedPorts.put(port.macAddress(), port);
Jian Li60312252018-05-10 18:40:32 +0900723 }
724 }
725 }
726
727 // TODO: should be extracted as an utility helper method sooner
728 private boolean isValidHost(Host host) {
729 return !host.ipAddresses().isEmpty() &&
730 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
731 host.annotations().value(ANNOTATION_PORT_ID) != null;
732 }
733 }
Jian Lif96685c2018-05-21 14:14:16 +0900734
735 private class InternalNodeEventListener implements OpenstackNodeListener {
736
737 @Override
738 public boolean isRelevant(OpenstackNodeEvent event) {
739 // do not allow to proceed without leadership
740 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900741 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900742 }
743
744 @Override
745 public void event(OpenstackNodeEvent event) {
746 OpenstackNode osNode = event.subject();
747 switch (event.type()) {
748 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900749 setDefaultArpRule(osNode, true);
750 setFloatingIpArpRuleForGateway(osNode, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900751
Jian Lif3a28b02018-06-11 21:29:13 +0900752 // initialize FloatingIp to Mac map
753 initFloatingIpMacMap();
754
Jian Lif96685c2018-05-21 14:14:16 +0900755 break;
756 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900757 setDefaultArpRule(osNode, false);
758 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900759 break;
760 default:
761 break;
762 }
763 }
764
765 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
766 switch (arpMode) {
767 case ARP_PROXY_MODE:
768 setDefaultArpRuleForProxyMode(osNode, install);
769 break;
770 case ARP_BROADCAST_MODE:
771 setDefaultArpRuleForBroadcastMode(osNode, install);
772 break;
773 default:
774 log.warn("Invalid ARP mode {}. Please use either " +
775 "broadcast or proxy mode.", arpMode);
776 break;
777 }
778 }
779
780 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
781 TrafficSelector selector = DefaultTrafficSelector.builder()
782 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
783 .build();
784
785 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
786 .punt()
787 .build();
788
789 osFlowRuleService.setRule(
790 appId,
791 osNode.intgBridge(),
792 selector,
793 treatment,
794 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900795 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900796 install
797 );
798 }
799
800 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
801 // we only match ARP_REPLY in gateway node, because controller
802 // somehow need to process ARP_REPLY which is issued from
803 // external router...
804 TrafficSelector selector = DefaultTrafficSelector.builder()
805 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
806 .matchArpOp(ARP.OP_REPLY)
807 .build();
808
809 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
810 .punt()
811 .build();
812
813 osFlowRuleService.setRule(
814 appId,
815 osNode.intgBridge(),
816 selector,
817 treatment,
818 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900819 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900820 install
821 );
822 }
823 }
Jian Li24ec59f2018-05-23 19:01:25 +0900824
825 private class InternalInstancePortListener implements InstancePortListener {
826
827 @Override
828 public boolean isRelevant(InstancePortEvent event) {
829 Set<NetFloatingIP> ips = osRouterService.floatingIps();
830 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
831 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
832
833 if (gateways.size() == 1) {
834 return false;
835 }
836
837 return fip != null && isAssociatedWithVM(osNetworkService, fip);
838 }
839
840 @Override
841 public void event(InstancePortEvent event) {
842 Set<NetFloatingIP> ips = osRouterService.floatingIps();
843 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
844 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
845
846 switch (event.type()) {
847 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
848
849 migrationPool.put(fip.getFloatingIpAddress(), event.subject().deviceId());
850
851 eventExecutor.execute(() -> {
852 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
853 gateways, true);
854 });
855 break;
856 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
857
858 DeviceId newDeviceId = migrationPool.get(fip.getFloatingIpAddress());
859 DeviceId oldDeviceId = event.subject().deviceId();
860 migrationPool.remove(fip.getFloatingIpAddress());
861
862 OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
863 OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
864
865 if (oldGw != null && oldGw.equals(newGw)) {
866 return;
867 }
868
869 eventExecutor.execute(() ->
870 setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
871 gateways, false));
872 break;
873 default:
874 break;
875 }
876 }
877 }
Daniel Park81a61a12016-02-26 08:24:44 +0900878}