blob: 3667d7c01b5459211e82eece107d2c2b8c41069a [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;
Jian Lif96685c2018-05-21 14:14:16 +090093import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
Jian Li60312252018-05-10 18:40:32 +090094import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
95import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090096import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090097import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
98import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
99import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Lia171a432018-06-11 11:52:11 +0900100import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900101import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +0900102import static org.slf4j.LoggerFactory.getLogger;
103
104/**
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900106 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700107@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900108public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900109
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700110 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900111
Hyunsun Moon44aac662017-02-18 02:07:01 +0900112 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
113 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900114 private static final String ARP_MODE = "arpMode";
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900118
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900121
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900123 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700124
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900126 protected OpenstackRouterService osRouterService;
127
daniel parkeeb8e042018-02-21 14:06:58 +0900128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900129 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130
Jian Li60312252018-05-10 18:40:32 +0900131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900132 protected InstancePortService instancePortService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li60312252018-05-10 18:40:32 +0900135 protected ClusterService clusterService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected LeadershipService leadershipService;
139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
141 protected OpenstackFlowRuleService osFlowRuleService;
142
143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li1064e4f2018-05-29 16:16:53 +0900144 protected OpenstackNetworkService osNetworkService;
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900147 protected HostService hostService;
Jian Li60312252018-05-10 18:40:32 +0900148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900150 protected ComponentConfigService configService;
Jian Li60312252018-05-10 18:40:32 +0900151
152 // TODO: need to find a way to unify aprMode and gatewayMac variables with
153 // that in SwitchingArpHandler
154 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Jian Li1478f292018-05-28 17:10:59 +0900155 label = "ARP processing mode, broadcast (default) | proxy ")
Jian Li60312252018-05-10 18:40:32 +0900156 protected String arpMode = DEFAULT_ARP_MODE_STR;
157
158 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
159
160 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
161 private final HostListener hostListener = new InternalHostListener();
Jian Lif96685c2018-05-21 14:14:16 +0900162 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li60312252018-05-10 18:40:32 +0900163
164 private ApplicationId appId;
165 private NodeId localNodeId;
Jian Lif3a28b02018-06-11 21:29:13 +0900166 private Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
167 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Jian Li60312252018-05-10 18:40:32 +0900168
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 private final ExecutorService eventExecutor = newSingleThreadExecutor(
170 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700171
Hyunsun Moon0d457362017-06-27 17:19:41 +0900172 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700173
174 @Activate
175 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900176 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
177 configService.registerProperties(getClass());
178 localNodeId = clusterService.getLocalNode().id();
Jian Li60312252018-05-10 18:40:32 +0900179 hostService.addListener(hostListener);
Jian Lif3a28b02018-06-11 21:29:13 +0900180 osRouterService.addListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900181 osNodeService.addListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900182 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700183 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
184 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900185 }
186
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700187 @Deactivate
188 protected void deactivate() {
189 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900190 hostService.removeListener(hostListener);
191 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900192 osNodeService.removeListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900193 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900194 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900195 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700196 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900197 }
198
Jian Li60312252018-05-10 18:40:32 +0900199 // TODO: need to find a way to unify aprMode and gatewayMac variables with
200 // that in SwitchingArpHandler
201 @Modified
202 void modified(ComponentContext context) {
203 Dictionary<?, ?> properties = context.getProperties();
204 String updateArpMode;
205
206 updateArpMode = Tools.get(properties, ARP_MODE);
207 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
208 arpMode = updateArpMode;
209 }
210
211 log.info("Modified");
212 }
213
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700214 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900215 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900216
217 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900218 if (log.isTraceEnabled()) {
219 log.trace("ARP request received from {} for {}",
220 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
221 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
222 }
223
224 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900225
226 MacAddress targetMac = null;
227
228 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
229 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
230 .findAny().orElse(null);
231
daniel park576969a2018-03-09 07:07:41 +0900232 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900233 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900234 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
235 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900236 }
237
238 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
239 targetMac = Constants.DEFAULT_GATEWAY_MAC;
240 }
241
242 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900243 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
244 return;
245 }
246
Jian Lia171a432018-06-11 11:52:11 +0900247 InstancePort instPort = instancePortService.instancePort(targetMac);
248
249 OpenstackNode gw = getGwByInstancePort(osNodeService.completeNodes(GATEWAY), instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900250
251 if (gw == null) {
252 return;
253 }
254
255 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
256 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
257 return;
258 }
259
daniel parkb5817102018-02-15 00:18:51 +0900260 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
261 targetMac, ethernet);
262
263 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900264 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900265
266 packetService.emit(new DefaultOutboundPacket(
267 context.inPacket().receivedFrom().deviceId(),
268 treatment,
269 ByteBuffer.wrap(ethReply.serialize())));
270
271 context.block();
Jian Li60312252018-05-10 18:40:32 +0900272 }
273
274 if (arp.getOpCode() == ARP.OP_REPLY) {
Jian Li14a79f22018-06-05 03:44:22 +0900275 ConnectPoint cp = context.inPacket().receivedFrom();
276 PortNumber receivedPortNum = cp.port();
277 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
278 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
279
280 log.debug("ARP reply ip: {}, mac: {}", spa, sha);
281
daniel parkb5817102018-02-15 00:18:51 +0900282 try {
Jian Li14a79f22018-06-05 03:44:22 +0900283
Jian Lid4066ea2018-06-07 01:44:45 +0900284 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
285 stream().map(r -> r.externalPeerRouterIp().toString()).collect(Collectors.toSet());
Jian Li14a79f22018-06-05 03:44:22 +0900286
Jian Lid4066ea2018-06-07 01:44:45 +0900287 // if SPA is NOT contained in existing external router IP set, we ignore it
288 if (!extRouterIps.contains(spa.toString())) {
Jian Li14a79f22018-06-05 03:44:22 +0900289 return;
290 }
291
292 OpenstackNode node = osNodeService.node(cp.deviceId());
293
294 if (node == null) {
295 return;
296 }
297
298 // we only handles the ARP-Reply message received by gateway node
299 if (node.type() != GATEWAY) {
300 return;
301 }
302
303 if (receivedPortNum.equals(node.uplinkPortNum())) {
304 osNetworkAdminService.updateExternalPeerRouterMac(spa, sha);
daniel parkb5817102018-02-15 00:18:51 +0900305 }
306 } catch (Exception e) {
Jian Li14a79f22018-06-05 03:44:22 +0900307 log.error("Exception occurred because of {}", e);
daniel parkb5817102018-02-15 00:18:51 +0900308 }
Daniel Park81a61a12016-02-26 08:24:44 +0900309 }
Daniel Park81a61a12016-02-26 08:24:44 +0900310 }
311
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700312 private class InternalPacketProcessor implements PacketProcessor {
313
314 @Override
315 public void process(PacketContext context) {
316 if (context.isHandled()) {
317 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900318 }
319
320 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
321 .stream().map(OpenstackNode::intgBridge)
322 .collect(Collectors.toSet());
323
324 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700325 // return if the packet is not from gateway nodes
326 return;
327 }
328
329 InboundPacket pkt = context.inPacket();
330 Ethernet ethernet = pkt.parsed();
331 if (ethernet != null &&
332 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900333 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700334 }
335 }
336 }
337
daniel parkeeb8e042018-02-21 14:06:58 +0900338 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900339 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900340 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900341 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900342 .flatMap(osPort -> osPort.getFixedIps().stream())
343 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900344 }
Jian Li60312252018-05-10 18:40:32 +0900345
Jian Li60312252018-05-10 18:40:32 +0900346 private void initFloatingIpMacMap() {
347 osRouterService.floatingIps().forEach(f -> {
348 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
349 Port port = osNetworkAdminService.port(f.getPortId());
350 if (port != null && port.getMacAddress() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900351 floatingIpMacMap.put(f.getFloatingIpAddress(),
352 MacAddress.valueOf(port.getMacAddress()));
Jian Li60312252018-05-10 18:40:32 +0900353 }
354 }
355 });
356 }
357
358 /**
359 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900360 *
361 * @param gateway gateway node
362 * @param install flow rule installation flag
363 */
364 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
365 if (arpMode.equals(ARP_BROADCAST_MODE)) {
366
367 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
368 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
369 finalGws.addAll(ImmutableSet.copyOf(completedGws));
370
371 if (install) {
372 if (completedGws.contains(gateway)) {
373 if (completedGws.size() > 1) {
374 finalGws.remove(gateway);
375 osRouterService.floatingIps().forEach(fip -> {
376 if (fip.getPortId() != null) {
377 setFloatingIpArpRule(fip, finalGws, false);
378 finalGws.add(gateway);
379 }
380 });
381 }
382 osRouterService.floatingIps().forEach(fip -> {
383 if (fip.getPortId() != null) {
384 setFloatingIpArpRule(fip, finalGws, true);
385 }
386 });
387 } else {
388 log.warn("Detected node should be included in completed gateway set");
389 }
390 } else {
391 if (!completedGws.contains(gateway)) {
392 finalGws.add(gateway);
393 osRouterService.floatingIps().forEach(fip -> {
394 if (fip.getPortId() != null) {
395 setFloatingIpArpRule(fip, finalGws, false);
396 }
397 });
398 finalGws.remove(gateway);
399 if (completedGws.size() >= 1) {
400 osRouterService.floatingIps().forEach(fip -> {
401 if (fip.getPortId() != null) {
402 setFloatingIpArpRule(fip, finalGws, true);
403 }
404 });
405 }
406 } else {
407 log.warn("Detected node should NOT be included in completed gateway set");
408 }
409 }
410 }
411 }
412
413 /**
414 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900415 * Note that, those rules will be only matched ARP_REQUEST packets,
416 * used for telling gateway node the mapped MAC address of requested IP,
417 * without the helps from controller.
418 *
419 * @param fip floating IP address
Jian Li1064e4f2018-05-29 16:16:53 +0900420 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900421 * @param install flow rule installation flag
422 */
Jian Li1064e4f2018-05-29 16:16:53 +0900423 private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
424 Set<OpenstackNode> gateways,
425 boolean install) {
Jian Li60312252018-05-10 18:40:32 +0900426 if (arpMode.equals(ARP_BROADCAST_MODE)) {
427
428 if (fip == null) {
429 log.warn("Failed to set ARP broadcast rule for floating IP");
430 return;
431 }
432
Jian Lif3a28b02018-06-11 21:29:13 +0900433 MacAddress targetMac;
434 InstancePort instPort;
Jian Li60312252018-05-10 18:40:32 +0900435
436 if (install) {
437 if (fip.getPortId() != null) {
Jian Lif3a28b02018-06-11 21:29:13 +0900438 String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
439 targetMac = MacAddress.valueOf(macString);
440 floatingIpMacMap.put(fip.getFloatingIpAddress(), targetMac);
Jian Li60312252018-05-10 18:40:32 +0900441 } else {
442 log.trace("Unknown target ARP request for {}, ignore it",
443 fip.getFloatingIpAddress());
444 return;
445 }
446 } else {
Jian Lif3a28b02018-06-11 21:29:13 +0900447 targetMac = floatingIpMacMap.get(fip.getFloatingIpAddress());
Jian Li60312252018-05-10 18:40:32 +0900448 }
449
Jian Lif3a28b02018-06-11 21:29:13 +0900450 instPort = instancePortService.instancePort(targetMac);
451
452 // in VM purge case, we will have null instance port
453 if (instPort == null) {
454 instPort = removedPorts.get(targetMac);
455 removedPorts.remove(targetMac);
456 }
Jian Li60312252018-05-10 18:40:32 +0900457
Jian Lia171a432018-06-11 11:52:11 +0900458 OpenstackNode gw = getGwByInstancePort(gateways, instPort);
Jian Li1064e4f2018-05-29 16:16:53 +0900459
460 if (gw == null) {
461 return;
462 }
463
Jian Li60312252018-05-10 18:40:32 +0900464 TrafficSelector selector = DefaultTrafficSelector.builder()
465 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
466 .matchArpOp(ARP.OP_REQUEST)
467 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
468 .build();
469
470 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
471 .setArpOp(ARP.OP_REPLY)
472 .setArpSha(targetMac)
473 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
474 .setOutput(PortNumber.IN_PORT)
475 .build();
476
Jian Li1064e4f2018-05-29 16:16:53 +0900477 osFlowRuleService.setRule(
478 appId,
479 gw.intgBridge(),
480 selector,
481 treatment,
482 PRIORITY_ARP_GATEWAY_RULE,
483 GW_COMMON_TABLE,
484 install
Jian Li60312252018-05-10 18:40:32 +0900485 );
486
487 if (install) {
488 log.info("Install ARP Rule for Floating IP {}",
489 fip.getFloatingIpAddress());
490 } else {
491 log.info("Uninstall ARP Rule for Floating IP {}",
492 fip.getFloatingIpAddress());
493 }
494 }
495 }
496
497 /**
498 * An internal router event listener, intended to install/uninstall
499 * ARP rules for forwarding packets created from floating IPs.
500 */
501 private class InternalRouterEventListener implements OpenstackRouterListener {
502
503 @Override
504 public boolean isRelevant(OpenstackRouterEvent event) {
505 // do not allow to proceed without leadership
506 NodeId leader = leadershipService.getLeader(appId.name());
507 return Objects.equals(localNodeId, leader);
508 }
509
510 @Override
511 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900512
513 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
514
Jian Li60312252018-05-10 18:40:32 +0900515 switch (event.type()) {
516 case OPENSTACK_ROUTER_CREATED:
517 eventExecutor.execute(() ->
518 // add a router with external gateway
519 setFakeGatewayArpRule(event.subject(), true)
520 );
521 break;
522 case OPENSTACK_ROUTER_REMOVED:
523 eventExecutor.execute(() ->
524 // remove a router with external gateway
525 setFakeGatewayArpRule(event.subject(), false)
526 );
527 break;
528 case OPENSTACK_ROUTER_GATEWAY_ADDED:
529 eventExecutor.execute(() ->
530 // add a gateway manually after adding a router
531 setFakeGatewayArpRule(event.externalGateway(), true)
532 );
533 break;
534 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
535 eventExecutor.execute(() ->
536 // remove a gateway from an existing router
537 setFakeGatewayArpRule(event.externalGateway(), false)
538 );
539 break;
540 case OPENSTACK_FLOATING_IP_ASSOCIATED:
541 eventExecutor.execute(() ->
542 // associate a floating IP with an existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900543 setFloatingIpArpRule(event.floatingIp(), completedGws, true)
Jian Li60312252018-05-10 18:40:32 +0900544 );
545 break;
546 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
547 eventExecutor.execute(() ->
548 // disassociate a floating IP with the existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900549 setFloatingIpArpRule(event.floatingIp(), completedGws, false)
Jian Li60312252018-05-10 18:40:32 +0900550 );
551 break;
552 case OPENSTACK_FLOATING_IP_CREATED:
553 eventExecutor.execute(() -> {
554 NetFloatingIP osFip = event.floatingIp();
555
556 // during floating IP creation, if the floating IP is
557 // associated with any port of VM, then we will set
558 // floating IP related ARP rules to gateway node
559 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900560 setFloatingIpArpRule(osFip, completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900561 }
562 });
563 break;
564 case OPENSTACK_FLOATING_IP_REMOVED:
565 eventExecutor.execute(() -> {
566 NetFloatingIP osFip = event.floatingIp();
567
568 // during floating IP deletion, if the floating IP is
569 // still associated with any port of VM, then we will
570 // remove floating IP related ARP rules from gateway node
571 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900572 setFloatingIpArpRule(event.floatingIp(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900573 }
574 });
575 break;
576 default:
577 // do nothing for the other events
578 break;
579 }
580 }
581
Jian Li4df657b2018-05-29 16:39:00 +0900582 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
583 return osNetworkAdminService.ports().stream()
584 .filter(port ->
585 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
586 .filter(port ->
587 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
588 .flatMap(port -> port.getFixedIps().stream())
589 .collect(Collectors.toSet());
590 }
591
Jian Li60312252018-05-10 18:40:32 +0900592 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
593 if (arpMode.equals(ARP_BROADCAST_MODE)) {
594
595 if (extGw == null) {
596 return;
597 }
598
Jian Li4df657b2018-05-29 16:39:00 +0900599 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900600
Jian Li4df657b2018-05-29 16:39:00 +0900601 ips.forEach(ip -> {
602 TrafficSelector selector = DefaultTrafficSelector.builder()
603 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
604 .matchArpOp(ARP.OP_REQUEST)
605 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
606 .build();
Jian Li60312252018-05-10 18:40:32 +0900607
Jian Li4df657b2018-05-29 16:39:00 +0900608 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
609 .setArpOp(ARP.OP_REPLY)
610 .setArpSha(MacAddress.valueOf(gatewayMac))
611 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
612 .setOutput(PortNumber.IN_PORT)
613 .build();
Jian Li60312252018-05-10 18:40:32 +0900614
Jian Li4df657b2018-05-29 16:39:00 +0900615 osNodeService.completeNodes(GATEWAY).forEach(n ->
616 osFlowRuleService.setRule(
617 appId,
618 n.intgBridge(),
619 selector,
620 treatment,
621 PRIORITY_ARP_GATEWAY_RULE,
622 GW_COMMON_TABLE,
623 install
624 )
625 );
Jian Li60312252018-05-10 18:40:32 +0900626
Jian Li4df657b2018-05-29 16:39:00 +0900627 if (install) {
628 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
629 } else {
630 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
631 }
632 });
Jian Li60312252018-05-10 18:40:32 +0900633 }
634 }
635
636 private void setFakeGatewayArpRule(Router router, boolean install) {
637 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
638 }
639 }
640
641 /**
642 * An internal host event listener, intended to uninstall
643 * ARP rules during host removal. Note that this is only valid when users
644 * remove host without disassociating floating IP with existing VM.
645 */
646 private class InternalHostListener implements HostListener {
647
648 @Override
649 public boolean isRelevant(HostEvent event) {
650 Host host = event.subject();
651 if (!isValidHost(host)) {
652 log.debug("Invalid host detected, ignore it {}", host);
653 return false;
654 }
655 return true;
656 }
657
658 @Override
659 public void event(HostEvent event) {
660 InstancePort instPort = HostBasedInstancePort.of(event.subject());
661 switch (event.type()) {
662 case HOST_REMOVED:
Jian Lif3a28b02018-06-11 21:29:13 +0900663 storeTempInstPort(instPort);
Jian Li60312252018-05-10 18:40:32 +0900664 break;
665 case HOST_UPDATED:
666 case HOST_ADDED:
667 default:
668 break;
669 }
670 }
671
Jian Lif3a28b02018-06-11 21:29:13 +0900672 private void storeTempInstPort(InstancePort port) {
Jian Li60312252018-05-10 18:40:32 +0900673 Set<NetFloatingIP> ips = osRouterService.floatingIps();
674 for (NetFloatingIP fip : ips) {
675 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
676 continue;
677 }
678 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
679 continue;
680 }
681 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
Jian Lif3a28b02018-06-11 21:29:13 +0900682 removedPorts.put(port.macAddress(), port);
Jian Li60312252018-05-10 18:40:32 +0900683 }
684 }
685 }
686
687 // TODO: should be extracted as an utility helper method sooner
688 private boolean isValidHost(Host host) {
689 return !host.ipAddresses().isEmpty() &&
690 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
691 host.annotations().value(ANNOTATION_PORT_ID) != null;
692 }
693 }
Jian Lif96685c2018-05-21 14:14:16 +0900694
695 private class InternalNodeEventListener implements OpenstackNodeListener {
696
697 @Override
698 public boolean isRelevant(OpenstackNodeEvent event) {
699 // do not allow to proceed without leadership
700 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900701 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
Jian Lif96685c2018-05-21 14:14:16 +0900702 }
703
704 @Override
705 public void event(OpenstackNodeEvent event) {
706 OpenstackNode osNode = event.subject();
707 switch (event.type()) {
708 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900709 setDefaultArpRule(osNode, true);
710 setFloatingIpArpRuleForGateway(osNode, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900711
Jian Lif3a28b02018-06-11 21:29:13 +0900712 // initialize FloatingIp to Mac map
713 initFloatingIpMacMap();
714
Jian Lif96685c2018-05-21 14:14:16 +0900715 break;
716 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900717 setDefaultArpRule(osNode, false);
718 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900719 break;
720 default:
721 break;
722 }
723 }
724
725 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
726 switch (arpMode) {
727 case ARP_PROXY_MODE:
728 setDefaultArpRuleForProxyMode(osNode, install);
729 break;
730 case ARP_BROADCAST_MODE:
731 setDefaultArpRuleForBroadcastMode(osNode, install);
732 break;
733 default:
734 log.warn("Invalid ARP mode {}. Please use either " +
735 "broadcast or proxy mode.", arpMode);
736 break;
737 }
738 }
739
740 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
741 TrafficSelector selector = DefaultTrafficSelector.builder()
742 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
743 .build();
744
745 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
746 .punt()
747 .build();
748
749 osFlowRuleService.setRule(
750 appId,
751 osNode.intgBridge(),
752 selector,
753 treatment,
754 PRIORITY_ARP_CONTROL_RULE,
755 DHCP_ARP_TABLE,
756 install
757 );
758 }
759
760 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
761 // we only match ARP_REPLY in gateway node, because controller
762 // somehow need to process ARP_REPLY which is issued from
763 // external router...
764 TrafficSelector selector = DefaultTrafficSelector.builder()
765 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
766 .matchArpOp(ARP.OP_REPLY)
767 .build();
768
769 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
770 .punt()
771 .build();
772
773 osFlowRuleService.setRule(
774 appId,
775 osNode.intgBridge(),
776 selector,
777 treatment,
778 PRIORITY_ARP_CONTROL_RULE,
779 DHCP_ARP_TABLE,
780 install
781 );
782 }
783 }
Daniel Park81a61a12016-02-26 08:24:44 +0900784}