blob: dced0df87beced05a821978ef22f64a26bd6d8b5 [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;
Hyunsun Moon0d457362017-06-27 17:19:41 +090042import org.onosproject.net.DeviceId;
Jian Li60312252018-05-10 18:40:32 +090043import org.onosproject.net.Host;
daniel parkb5817102018-02-15 00:18:51 +090044import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090045import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090046import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090047import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090048import org.onosproject.net.flow.TrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090049import org.onosproject.net.host.HostEvent;
50import org.onosproject.net.host.HostListener;
51import org.onosproject.net.host.HostService;
Daniel Park81a61a12016-02-26 08:24:44 +090052import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070053import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090054import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070055import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090056import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090057import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090058import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li1064e4f2018-05-29 16:16:53 +090059import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Li60312252018-05-10 18:40:32 +090060import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090061import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li1064e4f2018-05-29 16:16:53 +090062import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li60312252018-05-10 18:40:32 +090063import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
64import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090065import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090066import org.onosproject.openstacknode.api.OpenstackNode;
Jian Lif96685c2018-05-21 14:14:16 +090067import org.onosproject.openstacknode.api.OpenstackNodeEvent;
68import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090069import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090070import org.openstack4j.model.network.ExternalGateway;
Jian Li4df657b2018-05-29 16:39:00 +090071import org.openstack4j.model.network.IP;
daniel parkeeb8e042018-02-21 14:06:58 +090072import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090073import org.openstack4j.model.network.Port;
74import org.openstack4j.model.network.Router;
Jian Li60312252018-05-10 18:40:32 +090075import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090076import org.slf4j.Logger;
77
78import java.nio.ByteBuffer;
Jian Li60312252018-05-10 18:40:32 +090079import java.util.Dictionary;
80import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090081import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090082import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070083import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090084import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090085
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070086import static java.util.concurrent.Executors.newSingleThreadExecutor;
87import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090088import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
89import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
90import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
91import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Lif96685c2018-05-21 14:14:16 +090092import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
Jian Li60312252018-05-10 18:40:32 +090093import 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 Li1064e4f2018-05-29 16:16:53 +090099import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
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 Li60312252018-05-10 18:40:32 +0900146 protected ComponentConfigService configService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected HostService hostService;
150
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;
165 private Map<String, String> floatingIpMacMap = Maps.newConcurrentMap();
166
Hyunsun Moon44aac662017-02-18 02:07:01 +0900167 private final ExecutorService eventExecutor = newSingleThreadExecutor(
168 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700169
Hyunsun Moon0d457362017-06-27 17:19:41 +0900170 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700171
172 @Activate
173 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900174 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
175 configService.registerProperties(getClass());
176 localNodeId = clusterService.getLocalNode().id();
177 osRouterService.addListener(osRouterListener);
178 hostService.addListener(hostListener);
Jian Lif96685c2018-05-21 14:14:16 +0900179 osNodeService.addListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900180 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700181 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
182 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900183 }
184
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700185 @Deactivate
186 protected void deactivate() {
187 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900188 hostService.removeListener(hostListener);
189 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900190 osNodeService.removeListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900191 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900192 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900193 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700194 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900195 }
196
Jian Li60312252018-05-10 18:40:32 +0900197 // TODO: need to find a way to unify aprMode and gatewayMac variables with
198 // that in SwitchingArpHandler
199 @Modified
200 void modified(ComponentContext context) {
201 Dictionary<?, ?> properties = context.getProperties();
202 String updateArpMode;
203
204 updateArpMode = Tools.get(properties, ARP_MODE);
205 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
206 arpMode = updateArpMode;
207 }
208
209 log.info("Modified");
210 }
211
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700212 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900213 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900214
215 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900216 if (log.isTraceEnabled()) {
217 log.trace("ARP request received from {} for {}",
218 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
219 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
220 }
221
222 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900223
224 MacAddress targetMac = null;
225
226 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
227 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
228 .findAny().orElse(null);
229
daniel park576969a2018-03-09 07:07:41 +0900230 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900231 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900232 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
233 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900234 }
235
236 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
237 targetMac = Constants.DEFAULT_GATEWAY_MAC;
238 }
239
240 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900241 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
242 return;
243 }
244
Jian Li1064e4f2018-05-29 16:16:53 +0900245 OpenstackNode gw = getGwByTargetMac(osNodeService.completeNodes(GATEWAY), targetMac);
246
247 if (gw == null) {
248 return;
249 }
250
251 // if the ARP packet_in received from non-relevant GWs, we simply ignore it
252 if (!Objects.equals(gw.intgBridge(), context.inPacket().receivedFrom().deviceId())) {
253 return;
254 }
255
daniel parkb5817102018-02-15 00:18:51 +0900256 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
257 targetMac, ethernet);
258
259 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900260 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900261
262 packetService.emit(new DefaultOutboundPacket(
263 context.inPacket().receivedFrom().deviceId(),
264 treatment,
265 ByteBuffer.wrap(ethReply.serialize())));
266
267 context.block();
Jian Li60312252018-05-10 18:40:32 +0900268 }
269
270 if (arp.getOpCode() == ARP.OP_REPLY) {
daniel parkb5817102018-02-15 00:18:51 +0900271 PortNumber receivedPortNum = context.inPacket().receivedFrom().port();
272 log.debug("ARP reply ip: {}, mac: {}",
273 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
274 MacAddress.valueOf(arp.getSenderHardwareAddress()));
275 try {
276 if (receivedPortNum.equals(
Jian Li60312252018-05-10 18:40:32 +0900277 osNodeService.node(context.inPacket().receivedFrom()
278 .deviceId()).uplinkPortNum())) {
daniel park32b42202018-03-14 16:53:44 +0900279 osNetworkAdminService.updateExternalPeerRouterMac(
daniel parkb5817102018-02-15 00:18:51 +0900280 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
281 MacAddress.valueOf(arp.getSenderHardwareAddress()));
282 }
283 } catch (Exception e) {
284 log.error("Exception occurred because of {}", e.toString());
285 }
Daniel Park81a61a12016-02-26 08:24:44 +0900286 }
287
Daniel Park81a61a12016-02-26 08:24:44 +0900288 }
289
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700290 private class InternalPacketProcessor implements PacketProcessor {
291
292 @Override
293 public void process(PacketContext context) {
294 if (context.isHandled()) {
295 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900296 }
297
298 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
299 .stream().map(OpenstackNode::intgBridge)
300 .collect(Collectors.toSet());
301
302 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700303 // return if the packet is not from gateway nodes
304 return;
305 }
306
307 InboundPacket pkt = context.inPacket();
308 Ethernet ethernet = pkt.parsed();
309 if (ethernet != null &&
310 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900311 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700312 }
313 }
314 }
315
daniel parkeeb8e042018-02-21 14:06:58 +0900316 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900317 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900318 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900319 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900320 .flatMap(osPort -> osPort.getFixedIps().stream())
321 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900322 }
Jian Li60312252018-05-10 18:40:32 +0900323
324 // FIXME: need to find a way to invoke this method during node initialization
325 private void initFloatingIpMacMap() {
326 osRouterService.floatingIps().forEach(f -> {
327 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
328 Port port = osNetworkAdminService.port(f.getPortId());
329 if (port != null && port.getMacAddress() != null) {
330 floatingIpMacMap.put(f.getFloatingIpAddress(), port.getMacAddress());
331 }
332 }
333 });
334 }
335
336 /**
337 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li1064e4f2018-05-29 16:16:53 +0900338 *
339 * @param gateway gateway node
340 * @param install flow rule installation flag
341 */
342 private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
343 if (arpMode.equals(ARP_BROADCAST_MODE)) {
344
345 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
346 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
347 finalGws.addAll(ImmutableSet.copyOf(completedGws));
348
349 if (install) {
350 if (completedGws.contains(gateway)) {
351 if (completedGws.size() > 1) {
352 finalGws.remove(gateway);
353 osRouterService.floatingIps().forEach(fip -> {
354 if (fip.getPortId() != null) {
355 setFloatingIpArpRule(fip, finalGws, false);
356 finalGws.add(gateway);
357 }
358 });
359 }
360 osRouterService.floatingIps().forEach(fip -> {
361 if (fip.getPortId() != null) {
362 setFloatingIpArpRule(fip, finalGws, true);
363 }
364 });
365 } else {
366 log.warn("Detected node should be included in completed gateway set");
367 }
368 } else {
369 if (!completedGws.contains(gateway)) {
370 finalGws.add(gateway);
371 osRouterService.floatingIps().forEach(fip -> {
372 if (fip.getPortId() != null) {
373 setFloatingIpArpRule(fip, finalGws, false);
374 }
375 });
376 finalGws.remove(gateway);
377 if (completedGws.size() >= 1) {
378 osRouterService.floatingIps().forEach(fip -> {
379 if (fip.getPortId() != null) {
380 setFloatingIpArpRule(fip, finalGws, true);
381 }
382 });
383 }
384 } else {
385 log.warn("Detected node should NOT be included in completed gateway set");
386 }
387 }
388 }
389 }
390
391 /**
392 * Installs static ARP rules used in ARP BROAD_CAST mode.
Jian Li60312252018-05-10 18:40:32 +0900393 * Note that, those rules will be only matched ARP_REQUEST packets,
394 * used for telling gateway node the mapped MAC address of requested IP,
395 * without the helps from controller.
396 *
397 * @param fip floating IP address
Jian Li1064e4f2018-05-29 16:16:53 +0900398 * @param gateways a set of gateway nodes
Jian Li60312252018-05-10 18:40:32 +0900399 * @param install flow rule installation flag
400 */
Jian Li1064e4f2018-05-29 16:16:53 +0900401 private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
402 Set<OpenstackNode> gateways,
403 boolean install) {
Jian Li60312252018-05-10 18:40:32 +0900404 if (arpMode.equals(ARP_BROADCAST_MODE)) {
405
406 if (fip == null) {
407 log.warn("Failed to set ARP broadcast rule for floating IP");
408 return;
409 }
410
411 String macString;
412
413 if (install) {
414 if (fip.getPortId() != null) {
415 macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
416 floatingIpMacMap.put(fip.getFloatingIpAddress(), macString);
417 } else {
418 log.trace("Unknown target ARP request for {}, ignore it",
419 fip.getFloatingIpAddress());
420 return;
421 }
422 } else {
423 macString = floatingIpMacMap.get(fip.getFloatingIpAddress());
424 }
425
426 MacAddress targetMac = MacAddress.valueOf(macString);
427
Jian Li1064e4f2018-05-29 16:16:53 +0900428 OpenstackNode gw = getGwByTargetMac(gateways, targetMac);
429
430 if (gw == null) {
431 return;
432 }
433
Jian Li60312252018-05-10 18:40:32 +0900434 TrafficSelector selector = DefaultTrafficSelector.builder()
435 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
436 .matchArpOp(ARP.OP_REQUEST)
437 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
438 .build();
439
440 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
441 .setArpOp(ARP.OP_REPLY)
442 .setArpSha(targetMac)
443 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
444 .setOutput(PortNumber.IN_PORT)
445 .build();
446
Jian Li1064e4f2018-05-29 16:16:53 +0900447 osFlowRuleService.setRule(
448 appId,
449 gw.intgBridge(),
450 selector,
451 treatment,
452 PRIORITY_ARP_GATEWAY_RULE,
453 GW_COMMON_TABLE,
454 install
Jian Li60312252018-05-10 18:40:32 +0900455 );
456
457 if (install) {
458 log.info("Install ARP Rule for Floating IP {}",
459 fip.getFloatingIpAddress());
460 } else {
461 log.info("Uninstall ARP Rule for Floating IP {}",
462 fip.getFloatingIpAddress());
463 }
464 }
465 }
466
Jian Li1064e4f2018-05-29 16:16:53 +0900467 // a helper method
468 private OpenstackNode getGwByTargetMac(Set<OpenstackNode> gateways,
469 MacAddress targetMac) {
470 InstancePort instPort = instancePortService.instancePort(targetMac);
471 OpenstackNode gw = null;
472 if (instPort != null && instPort.deviceId() != null) {
473 gw = getGwByComputeDevId(gateways, instPort.deviceId());
474 }
475 return gw;
476 }
477
Jian Li60312252018-05-10 18:40:32 +0900478 /**
479 * An internal router event listener, intended to install/uninstall
480 * ARP rules for forwarding packets created from floating IPs.
481 */
482 private class InternalRouterEventListener implements OpenstackRouterListener {
483
484 @Override
485 public boolean isRelevant(OpenstackRouterEvent event) {
486 // do not allow to proceed without leadership
487 NodeId leader = leadershipService.getLeader(appId.name());
488 return Objects.equals(localNodeId, leader);
489 }
490
491 @Override
492 public void event(OpenstackRouterEvent event) {
Jian Li1064e4f2018-05-29 16:16:53 +0900493
494 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
495
Jian Li60312252018-05-10 18:40:32 +0900496 switch (event.type()) {
497 case OPENSTACK_ROUTER_CREATED:
498 eventExecutor.execute(() ->
499 // add a router with external gateway
500 setFakeGatewayArpRule(event.subject(), true)
501 );
502 break;
503 case OPENSTACK_ROUTER_REMOVED:
504 eventExecutor.execute(() ->
505 // remove a router with external gateway
506 setFakeGatewayArpRule(event.subject(), false)
507 );
508 break;
509 case OPENSTACK_ROUTER_GATEWAY_ADDED:
510 eventExecutor.execute(() ->
511 // add a gateway manually after adding a router
512 setFakeGatewayArpRule(event.externalGateway(), true)
513 );
514 break;
515 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
516 eventExecutor.execute(() ->
517 // remove a gateway from an existing router
518 setFakeGatewayArpRule(event.externalGateway(), false)
519 );
520 break;
521 case OPENSTACK_FLOATING_IP_ASSOCIATED:
522 eventExecutor.execute(() ->
523 // associate a floating IP with an existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900524 setFloatingIpArpRule(event.floatingIp(), completedGws, true)
Jian Li60312252018-05-10 18:40:32 +0900525 );
526 break;
527 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
528 eventExecutor.execute(() ->
529 // disassociate a floating IP with the existing VM
Jian Li1064e4f2018-05-29 16:16:53 +0900530 setFloatingIpArpRule(event.floatingIp(), completedGws, false)
Jian Li60312252018-05-10 18:40:32 +0900531 );
532 break;
533 case OPENSTACK_FLOATING_IP_CREATED:
534 eventExecutor.execute(() -> {
535 NetFloatingIP osFip = event.floatingIp();
536
537 // during floating IP creation, if the floating IP is
538 // associated with any port of VM, then we will set
539 // floating IP related ARP rules to gateway node
540 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900541 setFloatingIpArpRule(osFip, completedGws, true);
Jian Li60312252018-05-10 18:40:32 +0900542 }
543 });
544 break;
545 case OPENSTACK_FLOATING_IP_REMOVED:
546 eventExecutor.execute(() -> {
547 NetFloatingIP osFip = event.floatingIp();
548
549 // during floating IP deletion, if the floating IP is
550 // still associated with any port of VM, then we will
551 // remove floating IP related ARP rules from gateway node
552 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900553 setFloatingIpArpRule(event.floatingIp(), completedGws, false);
Jian Li60312252018-05-10 18:40:32 +0900554 }
555 });
556 break;
557 default:
558 // do nothing for the other events
559 break;
560 }
561 }
562
Jian Li4df657b2018-05-29 16:39:00 +0900563 private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
564 return osNetworkAdminService.ports().stream()
565 .filter(port ->
566 Objects.equals(port.getNetworkId(), extGw.getNetworkId()))
567 .filter(port ->
568 Objects.equals(port.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
569 .flatMap(port -> port.getFixedIps().stream())
570 .collect(Collectors.toSet());
571 }
572
Jian Li60312252018-05-10 18:40:32 +0900573 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
574 if (arpMode.equals(ARP_BROADCAST_MODE)) {
575
576 if (extGw == null) {
577 return;
578 }
579
Jian Li4df657b2018-05-29 16:39:00 +0900580 Set<IP> ips = getExternalGatewaySnatIps(extGw);
Jian Li60312252018-05-10 18:40:32 +0900581
Jian Li4df657b2018-05-29 16:39:00 +0900582 ips.forEach(ip -> {
583 TrafficSelector selector = DefaultTrafficSelector.builder()
584 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
585 .matchArpOp(ARP.OP_REQUEST)
586 .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
587 .build();
Jian Li60312252018-05-10 18:40:32 +0900588
Jian Li4df657b2018-05-29 16:39:00 +0900589 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
590 .setArpOp(ARP.OP_REPLY)
591 .setArpSha(MacAddress.valueOf(gatewayMac))
592 .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
593 .setOutput(PortNumber.IN_PORT)
594 .build();
Jian Li60312252018-05-10 18:40:32 +0900595
Jian Li4df657b2018-05-29 16:39:00 +0900596 osNodeService.completeNodes(GATEWAY).forEach(n ->
597 osFlowRuleService.setRule(
598 appId,
599 n.intgBridge(),
600 selector,
601 treatment,
602 PRIORITY_ARP_GATEWAY_RULE,
603 GW_COMMON_TABLE,
604 install
605 )
606 );
Jian Li60312252018-05-10 18:40:32 +0900607
Jian Li4df657b2018-05-29 16:39:00 +0900608 if (install) {
609 log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
610 } else {
611 log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
612 }
613 });
Jian Li60312252018-05-10 18:40:32 +0900614 }
615 }
616
617 private void setFakeGatewayArpRule(Router router, boolean install) {
618 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
619 }
620 }
621
622 /**
623 * An internal host event listener, intended to uninstall
624 * ARP rules during host removal. Note that this is only valid when users
625 * remove host without disassociating floating IP with existing VM.
626 */
627 private class InternalHostListener implements HostListener {
628
629 @Override
630 public boolean isRelevant(HostEvent event) {
631 Host host = event.subject();
632 if (!isValidHost(host)) {
633 log.debug("Invalid host detected, ignore it {}", host);
634 return false;
635 }
636 return true;
637 }
638
639 @Override
640 public void event(HostEvent event) {
641 InstancePort instPort = HostBasedInstancePort.of(event.subject());
642 switch (event.type()) {
643 case HOST_REMOVED:
644 removeArpRuleByInstancePort(instPort);
645 break;
646 case HOST_UPDATED:
647 case HOST_ADDED:
648 default:
649 break;
650 }
651 }
652
653 private void removeArpRuleByInstancePort(InstancePort port) {
654 Set<NetFloatingIP> ips = osRouterService.floatingIps();
655 for (NetFloatingIP fip : ips) {
656 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
657 continue;
658 }
659 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
660 continue;
661 }
662 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
663 eventExecutor.execute(() ->
Jian Li1064e4f2018-05-29 16:16:53 +0900664 setFloatingIpArpRule(fip,
665 osNodeService.completeNodes(GATEWAY), false)
Jian Li60312252018-05-10 18:40:32 +0900666 );
667 }
668 }
669 }
670
671 // TODO: should be extracted as an utility helper method sooner
672 private boolean isValidHost(Host host) {
673 return !host.ipAddresses().isEmpty() &&
674 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
675 host.annotations().value(ANNOTATION_PORT_ID) != null;
676 }
677 }
Jian Lif96685c2018-05-21 14:14:16 +0900678
679 private class InternalNodeEventListener implements OpenstackNodeListener {
680
681 @Override
682 public boolean isRelevant(OpenstackNodeEvent event) {
683 // do not allow to proceed without leadership
684 NodeId leader = leadershipService.getLeader(appId.name());
685 return Objects.equals(localNodeId, leader);
686 }
687
688 @Override
689 public void event(OpenstackNodeEvent event) {
690 OpenstackNode osNode = event.subject();
691 switch (event.type()) {
692 case OPENSTACK_NODE_COMPLETE:
693 if (osNode.type().equals(GATEWAY)) {
694 setDefaultArpRule(osNode, true);
Jian Li1064e4f2018-05-29 16:16:53 +0900695 setFloatingIpArpRuleForGateway(osNode, true);
Jian Lif96685c2018-05-21 14:14:16 +0900696 }
Jian Li1064e4f2018-05-29 16:16:53 +0900697
Jian Lif96685c2018-05-21 14:14:16 +0900698 break;
699 case OPENSTACK_NODE_INCOMPLETE:
700 if (osNode.type().equals(GATEWAY)) {
701 setDefaultArpRule(osNode, false);
Jian Li1064e4f2018-05-29 16:16:53 +0900702 setFloatingIpArpRuleForGateway(osNode, false);
Jian Lif96685c2018-05-21 14:14:16 +0900703 }
704 break;
705 default:
706 break;
707 }
708 }
709
710 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
711 switch (arpMode) {
712 case ARP_PROXY_MODE:
713 setDefaultArpRuleForProxyMode(osNode, install);
714 break;
715 case ARP_BROADCAST_MODE:
716 setDefaultArpRuleForBroadcastMode(osNode, install);
717 break;
718 default:
719 log.warn("Invalid ARP mode {}. Please use either " +
720 "broadcast or proxy mode.", arpMode);
721 break;
722 }
723 }
724
725 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
726 TrafficSelector selector = DefaultTrafficSelector.builder()
727 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
728 .build();
729
730 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
731 .punt()
732 .build();
733
734 osFlowRuleService.setRule(
735 appId,
736 osNode.intgBridge(),
737 selector,
738 treatment,
739 PRIORITY_ARP_CONTROL_RULE,
740 DHCP_ARP_TABLE,
741 install
742 );
743 }
744
745 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
746 // we only match ARP_REPLY in gateway node, because controller
747 // somehow need to process ARP_REPLY which is issued from
748 // external router...
749 TrafficSelector selector = DefaultTrafficSelector.builder()
750 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
751 .matchArpOp(ARP.OP_REPLY)
752 .build();
753
754 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
755 .punt()
756 .build();
757
758 osFlowRuleService.setRule(
759 appId,
760 osNode.intgBridge(),
761 selector,
762 treatment,
763 PRIORITY_ARP_CONTROL_RULE,
764 DHCP_ARP_TABLE,
765 install
766 );
767 }
768 }
Daniel Park81a61a12016-02-26 08:24:44 +0900769}