blob: 4cadb17438609c4287ef86e7df9cacd195115d38 [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 Li1064e4f2018-05-29 16:16:53 +090060import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090061import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090062import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090063import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090064import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
65import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090066import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090068import org.onosproject.openstacknode.api.OpenstackNodeEvent;
69import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090071import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090072import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090073import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090074import org.openstack4j.model.network.Port;
75import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090076import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090077import org.slf4j.Logger;
78
79import java.nio.ByteBuffer;
Jian Li60312252018-05-10 18:40:32 +090080import java.util.Dictionary;
81import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090082import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090083import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070084import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090085import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090086
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070087import static java.util.concurrent.Executors.newSingleThreadExecutor;
88import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090089import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
90import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
91import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
92import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
93import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
94import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090095import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090096import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
97import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
98import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Lia171a432018-06-11 11:52:11 +090099import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900100import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +0900101import static org.slf4j.LoggerFactory.getLogger;
102
103/**
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900105 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700106@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900107public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700109 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900110
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
112 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900113 private static final String ARP_MODE = "arpMode";
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900117
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900120
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900122 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700123
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900125 protected OpenstackRouterService osRouterService;
126
daniel parkeeb8e042018-02-21 14:06:58 +0900127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900128 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129
Jian Li60312252018-05-10 18:40:32 +0900130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900131 protected InstancePortService instancePortService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li60312252018-05-10 18:40:32 +0900134 protected ClusterService clusterService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected LeadershipService leadershipService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected OpenstackFlowRuleService osFlowRuleService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900143 protected OpenstackNetworkService osNetworkService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900146 protected HostService hostService;
Jian Li60312252018-05-10 18:40:32 +0900147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900149 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900150
151 // TODO: need to find a way to unify aprMode and gatewayMac variables with
152 // that in SwitchingArpHandler
153 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Jian Li1478f292018-05-28 17:10:59 +0900154 label = "ARP processing mode, broadcast (default) | proxy ")
Jian Li60312252018-05-10 18:40:32 +0900155 protected String arpMode = DEFAULT_ARP_MODE_STR;
156
157 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
158
159 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
160 private final HostListener hostListener = new InternalHostListener();
Jian Lif96685c2018-05-21 14:14:16 +0900161 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li60312252018-05-10 18:40:32 +0900162
163 private ApplicationId appId;
164 private NodeId localNodeId;
Jian Lif3a28b02018-06-11 21:29:13 +0900165 private Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
166 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Jian Li60312252018-05-10 18:40:32 +0900167
Hyunsun Moon44aac662017-02-18 02:07:01 +0900168 private final ExecutorService eventExecutor = newSingleThreadExecutor(
169 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700170
Hyunsun Moon0d457362017-06-27 17:19:41 +0900171 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700172
173 @Activate
174 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900175 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
176 configService.registerProperties(getClass());
177 localNodeId = clusterService.getLocalNode().id();
Jian Li60312252018-05-10 18:40:32 +0900178 hostService.addListener(hostListener);
Jian Lif3a28b02018-06-11 21:29:13 +0900179 osRouterService.addListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900180 osNodeService.addListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900181 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700182 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
183 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900184 }
185
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700186 @Deactivate
187 protected void deactivate() {
188 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900189 hostService.removeListener(hostListener);
190 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900191 osNodeService.removeListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900192 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900193 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900194 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700195 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900196 }
197
Jian Li60312252018-05-10 18:40:32 +0900198 // TODO: need to find a way to unify aprMode and gatewayMac variables with
199 // that in SwitchingArpHandler
200 @Modified
201 void modified(ComponentContext context) {
202 Dictionary<?, ?> properties = context.getProperties();
203 String updateArpMode;
204
205 updateArpMode = Tools.get(properties, ARP_MODE);
206 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
207 arpMode = updateArpMode;
208 }
209
210 log.info("Modified");
211 }
212
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700213 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900214 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900215
216 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900217 if (log.isTraceEnabled()) {
218 log.trace("ARP request received from {} for {}",
219 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
220 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
221 }
222
223 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900224
225 MacAddress targetMac = null;
226
227 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
228 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
229 .findAny().orElse(null);
230
daniel park576969a2018-03-09 07:07:41 +0900231 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900232 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900233 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
234 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900235 }
236
237 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
238 targetMac = Constants.DEFAULT_GATEWAY_MAC;
239 }
240
241 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900242 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
243 return;
244 }
245
Jian Lia171a432018-06-11 11:52:11 +0900246 InstancePort instPort = instancePortService.instancePort(targetMac);
247
248 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900249
250 if (gw == null) {
251 return;
252 }
253
254 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
255 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
256 return;
257 }
258
daniel parkb5817102018-02-15 00:18:51 +0900259 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
260 targetMac, ethernet);
261
262 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900263 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900264
265 packetService.emit(new DefaultOutboundPacket(
266 context.inPacket().receivedFrom().deviceId(),
267 treatment,
268 ByteBuffer.wrap(ethReply.serialize())));
269
270 context.block();
Jian Li60312252018-05-10 18:40:32 +0900271 }
272
273 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900274 ConnectPoint cp = context.inPacket().receivedFrom();
275 PortNumber receivedPortNum = cp.port();
276 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
277 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
278
279 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
280
daniel parkb5817102018-02-15 00:18:51 +0900281 try {
Jian Li14a79f22018-06-05 03:44:22 +0900282
Jian Lid4066ea2018-06-07 01:44:45 +0900283 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
284 stream().map(r -> r.externalPeerRouterIp().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900285
Jian Lid4066ea2018-06-07 01:44:45 +0900286 // if SPA is NOT contained in existing external router IP set, we ignore it
287 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900288 return;
289 }
290
291 OpenstackNode node = osNodeService.node(cp.deviceId());
292
293 if (node == null) {
294 return;
295 }
296
297 // we only handles the ARP-Reply message received by gateway node
298 if (node.type() != GATEWAY) {
299 return;
300 }
301
302 if (receivedPortNum.equals(node.uplinkPortNum())) {
303 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900304 }
305 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900306 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900307 }
Daniel Park81a61a12016-02-26 08:24:44 +0900308 }
Daniel Park81a61a12016-02-26 08:24:44 +0900309 }
310
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700311 private class InternalPacketProcessor implements PacketProcessor {
312
313 @Override
314 public void process(PacketContext context) {
315 if (context.isHandled()) {
316 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900317 }
318
319 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
320 .stream().map(OpenstackNode::intgBridge)
321 .collect(Collectors.toSet());
322
323 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700324 // return if the packet is not from gateway nodes
325 return;
326 }
327
328 InboundPacket pkt = context.inPacket();
329 Ethernet ethernet = pkt.parsed();
330 if (ethernet != null &&
331 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900332 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700333 }
334 }
335 }
336
daniel parkeeb8e042018-02-21 14:06:58 +0900337 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900338 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900339 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900340 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900341 .flatMap(osPort -> osPort.getFixedIps().stream())
342 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900343 }
Jian Li60312252018-05-10 18:40:32 +0900344
Jian Li60312252018-05-10 18:40:32 +0900345 private void initFloatingIpMacMap() {
346 osRouterService.floatingIps().forEach(f -> {
347 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
348 Port port = osNetworkAdminService.port(f.getPortId());
349 if (port != null && port.getMacAddress() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900350 floatingIpMacMap.put(f.getFloatingIpAddress(),
351 MacAddress.valueOf(port.getMacAddress()));
Jian Li60312252018-05-10 18:40:32 +0900352 }
353 }
354 });
355 }
356
357 /**
358 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900359 *
360 * @param gateway gateway node
361 * @param install flow rule installation flag
362 */
363 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
364 if (arpMode.equals(ARP_BROADCAST_MODE)) {
365
366 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
367 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
368 finalGws.addAll(ImmutableSet.copyOf(completedGws));
369
370 if (install) {
371 if (completedGws.contains(gateway)) {
372 if (completedGws.size() > 1) {
373 finalGws.remove(gateway);
374 osRouterService.floatingIps().forEach(fip -> {
375 if (fip.getPortId() != null) {
376 setFloatingIpArpRule(fip, finalGws, false);
377 finalGws.add(gateway);
378 }
379 });
380 }
381 osRouterService.floatingIps().forEach(fip -> {
382 if (fip.getPortId() != null) {
383 setFloatingIpArpRule(fip, finalGws, true);
384 }
385 });
386 } else {
387 log.warn("Detected node should be included in completed gateway set");
388 }
389 } else {
390 if (!completedGws.contains(gateway)) {
391 finalGws.add(gateway);
392 osRouterService.floatingIps().forEach(fip -> {
393 if (fip.getPortId() != null) {
394 setFloatingIpArpRule(fip, finalGws, false);
395 }
396 });
397 finalGws.remove(gateway);
398 if (completedGws.size() >= 1) {
399 osRouterService.floatingIps().forEach(fip -> {
400 if (fip.getPortId() != null) {
401 setFloatingIpArpRule(fip, finalGws, true);
402 }
403 });
404 }
405 } else {
406 log.warn("Detected node should NOT be included in completed gateway set");
407 }
408 }
409 }
410 }
411
412 /**
413 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900414 * Note that, those rules will be only matched ARP_REQUEST packets,
415 * used for telling gateway node the mapped MAC address of requested IP,
416 * without the helps from controller.
417 *
418 * @param fip floating IP address
Jian Li1064e4f2018-05-29 16:16:53 +0900419 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900420 * @param install flow rule installation flag
421 */
Jian Li1064e4f2018-05-29 16:16:53 +0900422 private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
423 Set<OpenstackNode> gateways,
424 boolean install) {
Jian Li60312252018-05-10 18:40:32 +0900425 if (arpMode.equals(ARP_BROADCAST_MODE)) {
426
427 if (fip == null) {
428 log.warn("Failed to set ARP broadcast rule for floating IP");
429 return;
430 }
431
Jian Lif3a28b02018-06-11 21:29:13 +0900432 MacAddress targetMac;
433 InstancePort instPort;
Jian Li60312252018-05-10 18:40:32 +0900434
435 if (install) {
436 if (fip.getPortId() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900437 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
438 targetMac = MacAddress.valueOf(macString);
439 floatingIpMacMap.put(fip.getFloatingIpAddress(), targetMac);
Jian Li60312252018-05-10 18:40:32 +0900440 } else {
441 log.trace("Unknown target ARP request for {}, ignore it",
442 fip.getFloatingIpAddress());
443 return;
444 }
445 } else {
Jian Lif3a28b02018-06-11 21:29:13 +0900446 targetMac = floatingIpMacMap.get(fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900447 }
448
Jian Lif3a28b02018-06-11 21:29:13 +0900449 instPort = instancePortService.instancePort(targetMac);
450
451 // in VM purge case, we will have null instance port
452 if (instPort == null) {
453 instPort = removedPorts.get(targetMac);
454 removedPorts.remove(targetMac);
455 }
Jian Li60312252018-05-10 18:40:32 +0900456
Jian Lia171a432018-06-11 11:52:11 +0900457 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900458
459 if (gw == null) {
460 return;
461 }
462
Jian Li60312252018-05-10 18:40:32 +0900463 TrafficSelector selector = DefaultTrafficSelector.builder()
464 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
465 .matchArpOp(ARP.OP_REQUEST)
466 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
467 .build();
468
469 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
470 .setArpOp(ARP.OP_REPLY)
471 .setArpSha(targetMac)
472 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
473 .setOutput(PortNumber.IN_PORT)
474 .build();
475
Jian Li1064e4f2018-05-29 16:16:53 +0900476 osFlowRuleService.setRule(
477 appId,
478 gw.intgBridge(),
479 selector,
480 treatment,
481 PRIORITY_ARP_GATEWAY_RULE,
482 GW_COMMON_TABLE,
483 install
Jian Li60312252018-05-10 18:40:32 +0900484 );
485
486 if (install) {
487 log.info("Install ARP Rule for Floating IP {}",
488 fip.getFloatingIpAddress());
489 } else {
490 log.info("Uninstall ARP Rule for Floating IP {}",
491 fip.getFloatingIpAddress());
492 }
493 }
494 }
495
496 /**
497 * An internal router event listener, intended to install/uninstall
498 * ARP rules for forwarding packets created from floating IPs.
499 */
500 private class InternalRouterEventListener implements OpenstackRouterListener {
501
502 @Override
503 public boolean isRelevant(OpenstackRouterEvent event) {
504 // do not allow to proceed without leadership
505 NodeId leader = leadershipService.getLeader(appId.name());
506 return Objects.equals(localNodeId, leader);
507 }
508
509 @Override
510 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900511
512 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
513
Jian Li60312252018-05-10 18:40:32 +0900514 switch (event.type()) {
515 case OPENSTACK_ROUTER_CREATED:
516 eventExecutor.execute(() ->
517 // add a router with external gateway
518 setFakeGatewayArpRule(event.subject(), true)
519 );
520 break;
521 case OPENSTACK_ROUTER_REMOVED:
522 eventExecutor.execute(() ->
523 // remove a router with external gateway
524 setFakeGatewayArpRule(event.subject(), false)
525 );
526 break;
527 case OPENSTACK_ROUTER_GATEWAY_ADDED:
528 eventExecutor.execute(() ->
529 // add a gateway manually after adding a router
530 setFakeGatewayArpRule(event.externalGateway(), true)
531 );
532 break;
533 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
534 eventExecutor.execute(() ->
535 // remove a gateway from an existing router
536 setFakeGatewayArpRule(event.externalGateway(), false)
537 );
538 break;
539 case OPENSTACK_FLOATING_IP_ASSOCIATED:
540 eventExecutor.execute(() ->
541 // associate a floating IP with an existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900542 setFloatingIpArpRule(event.floatingIp(), completedGws, true)
Jian Li60312252018-05-10 18:40:32 +0900543 );
544 break;
545 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
546 eventExecutor.execute(() ->
547 // disassociate a floating IP with the existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900548 setFloatingIpArpRule(event.floatingIp(), completedGws, false)
Jian Li60312252018-05-10 18:40:32 +0900549 );
550 break;
551 case OPENSTACK_FLOATING_IP_CREATED:
552 eventExecutor.execute(() -> {
553 NetFloatingIP osFip = event.floatingIp();
554
555 // during floating IP creation, if the floating IP is
556 // associated with any port of VM, then we will set
557 // floating IP related ARP rules to gateway node
558 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900559 setFloatingIpArpRule(osFip, completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900560 }
561 });
562 break;
563 case OPENSTACK_FLOATING_IP_REMOVED:
564 eventExecutor.execute(() -> {
565 NetFloatingIP osFip = event.floatingIp();
566
567 // during floating IP deletion, if the floating IP is
568 // still associated with any port of VM, then we will
569 // remove floating IP related ARP rules from gateway node
570 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900571 setFloatingIpArpRule(event.floatingIp(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900572 }
573 });
574 break;
575 default:
576 // do nothing for the other events
577 break;
578 }
579 }
580
Jian Li4df657b2018-05-29 16:39:00 +0900581 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
582 return osNetworkAdminService.ports().stream()
583 .filter(port ->
584 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
585 .filter(port ->
586 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
587 .flatMap(port -> port.getFixedIps().stream())
588 .collect(Collectors.toSet());
589 }
590
Jian Li60312252018-05-10 18:40:32 +0900591 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
592 if (arpMode.equals(ARP_BROADCAST_MODE)) {
593
594 if (extGw == null) {
595 return;
596 }
597
Jian Li4df657b2018-05-29 16:39:00 +0900598 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900599
Jian Li4df657b2018-05-29 16:39:00 +0900600 ips.forEach(ip -> {
601 TrafficSelector selector = DefaultTrafficSelector.builder()
602 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
603 .matchArpOp(ARP.OP_REQUEST)
604 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
605 .build();
Jian Li60312252018-05-10 18:40:32 +0900606
Jian Li4df657b2018-05-29 16:39:00 +0900607 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
608 .setArpOp(ARP.OP_REPLY)
609 .setArpSha(MacAddress.valueOf(gatewayMac))
610 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
611 .setOutput(PortNumber.IN_PORT)
612 .build();
Jian Li60312252018-05-10 18:40:32 +0900613
Jian Li4df657b2018-05-29 16:39:00 +0900614 osNodeService.completeNodes(GATEWAY).forEach(n ->
615 osFlowRuleService.setRule(
616 appId,
617 n.intgBridge(),
618 selector,
619 treatment,
620 PRIORITY_ARP_GATEWAY_RULE,
621 GW_COMMON_TABLE,
622 install
623 )
624 );
Jian Li60312252018-05-10 18:40:32 +0900625
Jian Li4df657b2018-05-29 16:39:00 +0900626 if (install) {
627 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
628 } else {
629 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
630 }
631 });
Jian Li60312252018-05-10 18:40:32 +0900632 }
633 }
634
635 private void setFakeGatewayArpRule(Router router, boolean install) {
636 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
637 }
638 }
639
640 /**
641 * An internal host event listener, intended to uninstall
642 * ARP rules during host removal. Note that this is only valid when users
643 * remove host without disassociating floating IP with existing VM.
644 */
645 private class InternalHostListener implements HostListener {
646
647 @Override
648 public boolean isRelevant(HostEvent event) {
649 Host host = event.subject();
650 if (!isValidHost(host)) {
651 log.debug("Invalid host detected, ignore it {}", host);
652 return false;
653 }
654 return true;
655 }
656
657 @Override
658 public void event(HostEvent event) {
659 InstancePort instPort = HostBasedInstancePort.of(event.subject());
660 switch (event.type()) {
661 case HOST_REMOVED:
Jian Lif3a28b02018-06-11 21:29:13 +0900662 storeTempInstPort(instPort);
Jian Li60312252018-05-10 18:40:32 +0900663 break;
664 case HOST_UPDATED:
665 case HOST_ADDED:
666 default:
667 break;
668 }
669 }
670
Jian Lif3a28b02018-06-11 21:29:13 +0900671 private void storeTempInstPort(InstancePort port) {
Jian Li60312252018-05-10 18:40:32 +0900672 Set<NetFloatingIP> ips = osRouterService.floatingIps();
673 for (NetFloatingIP fip : ips) {
674 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
675 continue;
676 }
677 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
678 continue;
679 }
680 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
Jian Lif3a28b02018-06-11 21:29:13 +0900681 removedPorts.put(port.macAddress(), port);
Jian Li60312252018-05-10 18:40:32 +0900682 }
683 }
684 }
685
686 // TODO: should be extracted as an utility helper method sooner
687 private boolean isValidHost(Host host) {
688 return !host.ipAddresses().isEmpty() &&
689 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
690 host.annotations().value(ANNOTATION_PORT_ID) != null;
691 }
692 }
Jian Lif96685c2018-05-21 14:14:16 +0900693
694 private class InternalNodeEventListener implements OpenstackNodeListener {
695
696 @Override
697 public boolean isRelevant(OpenstackNodeEvent event) {
698 // do not allow to proceed without leadership
699 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900700 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900701 }
702
703 @Override
704 public void event(OpenstackNodeEvent event) {
705 OpenstackNode osNode = event.subject();
706 switch (event.type()) {
707 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900708 setDefaultArpRule(osNode, true);
709 setFloatingIpArpRuleForGateway(osNode, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900710
Jian Lif3a28b02018-06-11 21:29:13 +0900711 // initialize FloatingIp to Mac map
712 initFloatingIpMacMap();
713
Jian Lif96685c2018-05-21 14:14:16 +0900714 break;
715 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900716 setDefaultArpRule(osNode, false);
717 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900718 break;
719 default:
720 break;
721 }
722 }
723
724 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
725 switch (arpMode) {
726 case ARP_PROXY_MODE:
727 setDefaultArpRuleForProxyMode(osNode, install);
728 break;
729 case ARP_BROADCAST_MODE:
730 setDefaultArpRuleForBroadcastMode(osNode, install);
731 break;
732 default:
733 log.warn("Invalid ARP mode {}. Please use either " +
734 "broadcast or proxy mode.", arpMode);
735 break;
736 }
737 }
738
739 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
740 TrafficSelector selector = DefaultTrafficSelector.builder()
741 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
742 .build();
743
744 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
745 .punt()
746 .build();
747
748 osFlowRuleService.setRule(
749 appId,
750 osNode.intgBridge(),
751 selector,
752 treatment,
753 PRIORITY_ARP_CONTROL_RULE,
Jian Li8abf2fe2018-06-12 18:42:30 +0900754 GW_COMMON_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900755 install
756 );
757 }
758
759 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
760 // we only match ARP_REPLY in gateway node, because controller
761 // somehow need to process ARP_REPLY which is issued from
762 // external router...
763 TrafficSelector selector = DefaultTrafficSelector.builder()
764 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
765 .matchArpOp(ARP.OP_REPLY)
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 }
Daniel Park81a61a12016-02-26 08:24:44 +0900783}