blob: 3f72375dcd2c254624bfb5d858813835873bac03 [file] [log] [blame]
Hyunsun Moonb974fca2016-06-30 21:20:39 -07001/*
Jian Li8e7e6cd2018-03-30 13:33:08 +09002 * Copyright 2016-present Open Networking Foundation
3 *
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;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070017
18import com.google.common.base.Strings;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.ARP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090027import org.onlab.packet.EthType;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070028import org.onlab.packet.Ethernet;
29import org.onlab.packet.Ip4Address;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.util.Tools;
Hyunsun Moon44aac662017-02-18 02:07:01 +090033import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090034import org.onosproject.cfg.ConfigProperty;
Jian Lieae12362018-04-10 18:48:32 +090035import org.onosproject.cluster.ClusterService;
36import org.onosproject.cluster.LeadershipService;
37import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Jian Lieae12362018-04-10 18:48:32 +090040import org.onosproject.mastership.MastershipService;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.device.DeviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090043import org.onosproject.net.flow.DefaultTrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070044import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090045import org.onosproject.net.flow.TrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070046import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.packet.DefaultOutboundPacket;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090051import org.onosproject.openstacknetworking.api.InstancePort;
Jian Lieae12362018-04-10 18:48:32 +090052import org.onosproject.openstacknetworking.api.InstancePortEvent;
53import org.onosproject.openstacknetworking.api.InstancePortListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Lieae12362018-04-10 18:48:32 +090055import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090056import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
57import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
58import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lieae12362018-04-10 18:48:32 +090059import org.onosproject.openstacknode.api.OpenstackNode;
60import org.onosproject.openstacknode.api.OpenstackNodeEvent;
61import org.onosproject.openstacknode.api.OpenstackNodeListener;
62import org.onosproject.openstacknode.api.OpenstackNodeService;
63import org.openstack4j.model.network.Network;
64import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090065import org.openstack4j.model.network.Subnet;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070066import org.osgi.service.component.ComponentContext;
67import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069
Hyunsun Moonb974fca2016-06-30 21:20:39 -070070import java.nio.ByteBuffer;
71import java.util.Dictionary;
Jian Lieae12362018-04-10 18:48:32 +090072import java.util.Objects;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070073import java.util.Set;
Jian Li32b03622018-11-06 17:54:24 +090074import java.util.concurrent.ExecutorService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070075
76import static com.google.common.base.Preconditions.checkNotNull;
Jian Li32b03622018-11-06 17:54:24 +090077import static java.util.concurrent.Executors.newSingleThreadExecutor;
78import static org.onlab.util.Tools.groupedThreads;
Jian Lieae12362018-04-10 18:48:32 +090079import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
80import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
Jian Li5c09e212018-10-24 18:23:58 +090081import static org.onosproject.openstacknetworking.api.Constants.ARP_TABLE;
Jian Lieae12362018-04-10 18:48:32 +090082import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
Hyunsun Moon44aac662017-02-18 02:07:01 +090083import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
84import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lieae12362018-04-10 18:48:32 +090085import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li5c09e212018-10-24 18:23:58 +090086import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_FLOOD_RULE;
Jian Lieae12362018-04-10 18:48:32 +090087import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
88import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
89import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
Jian Lic2403592018-07-18 12:56:45 +090090import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Li7f70bb72018-07-06 23:35:30 +090091import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090092import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090093import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
94import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070095
96/**
97 * Handles ARP packet from VMs.
98 */
99@Component(immediate = true)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900100public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700101
102 private final Logger log = LoggerFactory.getLogger(getClass());
103
104 private static final String GATEWAY_MAC = "gatewayMac";
Jian Lieae12362018-04-10 18:48:32 +0900105 private static final String ARP_MODE = "arpMode";
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800108 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800111 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900114 OpenstackFlowRuleService osFlowRuleService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800117 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900120 ClusterService clusterService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 LeadershipService leadershipService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 DeviceService deviceService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 MastershipService mastershipService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800132 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800135 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700136
Jian Lieae12362018-04-10 18:48:32 +0900137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected OpenstackNodeService osNodeService;
139
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700140 @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700141 label = "Fake MAC address for virtual network subnet gateway")
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700142 private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700143
Jian Lieae12362018-04-10 18:48:32 +0900144 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900145 label = "ARP processing mode, broadcast | proxy (default)")
Jian Lieae12362018-04-10 18:48:32 +0900146 protected String arpMode = DEFAULT_ARP_MODE_STR;
147
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700148 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900149 private final InternalOpenstackNetworkListener osNetworkListener =
150 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900151 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
152 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
153
Jian Li32b03622018-11-06 17:54:24 +0900154 private final ExecutorService eventExecutor = newSingleThreadExecutor(
155 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
156
Hyunsun Moon44aac662017-02-18 02:07:01 +0900157
158 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900159 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700160
161 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800162 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900163 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
164 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900165 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900167 osNodeService.addListener(osNodeListener);
168 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700169 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900170
171 instancePortService.addListener(instancePortListener);
172
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700174 }
175
176 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800177 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700178 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900179 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900180 osNodeService.removeListener(osNodeListener);
181 instancePortService.removeListener(instancePortListener);
182 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900183 configService.unregisterProperties(getClass(), false);
Jian Li32b03622018-11-06 17:54:24 +0900184 eventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900185
186 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700187 }
188
189 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800190 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900191 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900192
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700193 log.info("Modified");
194 }
195
Jian Li7f70bb72018-07-06 23:35:30 +0900196 private String getArpMode() {
197 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
198 return getPropertyValue(properties, ARP_MODE);
199 }
200
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700201 /**
202 * Processes ARP request packets.
203 * It checks if the target IP is owned by a known host first and then ask to
204 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
205 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900206 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700207 * @param ethPacket ethernet packet
208 */
209 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900210
211 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900212 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900213 return;
214 }
215
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700216 ARP arpPacket = (ARP) ethPacket.getPayload();
217 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
218 return;
219 }
220
Hyunsun Moon44aac662017-02-18 02:07:01 +0900221 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
222 if (srcInstPort == null) {
223 log.trace("Failed to find source instance port(MAC:{})",
224 ethPacket.getSourceMAC());
225 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700226 }
227
Hyunsun Moon44aac662017-02-18 02:07:01 +0900228 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Daniel Park4d7f88b2018-09-19 19:03:38 +0900229
Jian Liac30e272018-10-18 23:08:03 +0900230 MacAddress replyMac = isGatewayIp(targetIp) ? MacAddress.valueOf(gatewayMac) :
Hyunsun Moon44aac662017-02-18 02:07:01 +0900231 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700232 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900233 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700234 return;
235 }
236
237 Ethernet ethReply = ARP.buildArpReply(
238 targetIp.getIp4Address(),
239 replyMac,
240 ethPacket);
241
242 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
243 .setOutput(context.inPacket().receivedFrom().port())
244 .build();
245
246 packetService.emit(new DefaultOutboundPacket(
247 context.inPacket().receivedFrom().deviceId(),
248 treatment,
249 ByteBuffer.wrap(ethReply.serialize())));
250 }
251
Jian Liac30e272018-10-18 23:08:03 +0900252 /**
253 * Denotes whether the given target IP is gateway IP.
254 *
255 * @param targetIp target IP address
256 * @return true if the given targetIP is gateway IP, false otherwise.
257 */
258 private boolean isGatewayIp(IpAddress targetIp) {
Daniel Park4d7f88b2018-09-19 19:03:38 +0900259 return osNetworkService.subnets().stream()
Jian Liac30e272018-10-18 23:08:03 +0900260 .filter(Objects::nonNull)
261 .filter(subnet -> subnet.getGateway() != null)
Daniel Park4d7f88b2018-09-19 19:03:38 +0900262 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
263 }
264
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700265 /**
266 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900267 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700268 *
269 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900270 * @param osNetId openstack network id of the source instance port
271 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700272 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900273 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700274 checkNotNull(targetIp);
275
Hyunsun Moon44aac662017-02-18 02:07:01 +0900276 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
277 if (instPort != null) {
278 log.trace("Found MAC from host service for {}", targetIp);
279 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700280 } else {
281 return MacAddress.NONE;
282 }
283 }
284
Jian Lieae12362018-04-10 18:48:32 +0900285 /**
Daniel Park613ac372018-06-28 14:30:11 +0900286 * Installs flow rules which convert ARP request packet into ARP reply
287 * by adding a fake gateway MAC address as Source Hardware Address.
288 *
289 * @param osSubnet openstack subnet
290 * @param install flag which indicates whether to install rule or remove rule
291 */
292 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
293
Jian Li7f70bb72018-07-06 23:35:30 +0900294 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li8e365bd2018-10-12 22:09:03 +0900295
296 // do not remove fake gateway ARP rules, if there is another gateway
297 // which has the same subnet that to be removed
298 // this only occurs if we have duplicated subnets associated with
299 // different networks
300 if (!install) {
301 long numOfDupGws = osNetworkService.subnets().stream()
302 .filter(s -> !s.getId().equals(osSubnet.getId()))
Jian Li7ce775a2018-10-18 07:24:53 +0900303 .filter(s -> s.getGateway() != null)
Jian Li8e365bd2018-10-12 22:09:03 +0900304 .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
305 .count();
306 if (numOfDupGws > 0) {
307 return;
308 }
309 }
310
Daniel Park613ac372018-06-28 14:30:11 +0900311 String gateway = osSubnet.getGateway();
Daniel Park6a2d95e2018-11-05 18:50:16 +0900312 if (gateway == null) {
313 return;
314 }
Daniel Park613ac372018-06-28 14:30:11 +0900315
316 TrafficSelector selector = DefaultTrafficSelector.builder()
317 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
318 .matchArpOp(ARP.OP_REQUEST)
319 .matchArpTpa(Ip4Address.valueOf(gateway))
320 .build();
321
322 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
323 .setArpOp(ARP.OP_REPLY)
324 .setArpSha(MacAddress.valueOf(gatewayMac))
325 .setArpSpa(Ip4Address.valueOf(gateway))
326 .setOutput(PortNumber.IN_PORT)
327 .build();
328
329 if (osNode == null) {
330 osNodeService.completeNodes(COMPUTE).forEach(n ->
331 osFlowRuleService.setRule(
332 appId,
333 n.intgBridge(),
334 selector,
335 treatment,
336 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900337 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900338 install
339 )
340 );
341 } else {
342 osFlowRuleService.setRule(
343 appId,
344 osNode.intgBridge(),
345 selector,
346 treatment,
347 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900348 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900349 install
350 );
351 }
352
353 }
354 }
355
356 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900357 * Installs flow rules to match ARP request packets.
358 *
359 * @param port instance port
360 * @param install installation flag
361 */
362 private void setArpRequestRule(InstancePort port, boolean install) {
363 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
364
365 switch (type) {
366 case VXLAN:
367 setRemoteArpRequestRuleForVxlan(port, install);
368 break;
369 case VLAN:
370 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
371 // there is no need to add any flow rules to handle ARP request
372 break;
373 default:
374 break;
375 }
376 }
377
378 /**
379 * Installs flow rules to match ARP reply packets.
380 *
381 * @param port instance port
382 * @param install installation flag
383 */
384 private void setArpReplyRule(InstancePort port, boolean install) {
385 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
386
387 switch (type) {
388 case VXLAN:
389 setArpReplyRuleForVxlan(port, install);
390 break;
391 case VLAN:
392 setArpReplyRuleForVlan(port, install);
393 break;
394 default:
395 break;
396 }
397 }
398
399 /**
400 * Installs flow rules to match ARP request packets only for VxLAN.
401 *
402 * @param port instance port
403 * @param install installation flag
404 */
405 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
406
407 OpenstackNode localNode = osNodeService.node(port.deviceId());
408
Jian Li5c09e212018-10-24 18:23:58 +0900409 String segId = osNetworkService.segmentId(port.networkId());
410
Jian Li7f70bb72018-07-06 23:35:30 +0900411 TrafficSelector selector = DefaultTrafficSelector.builder()
412 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
413 .matchArpOp(ARP.OP_REQUEST)
414 .matchArpTpa(port.ipAddress().getIp4Address())
Jian Li5c09e212018-10-24 18:23:58 +0900415 .matchTunnelId(Long.valueOf(segId))
Jian Li7f70bb72018-07-06 23:35:30 +0900416 .build();
417
418 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
419 }
420
421 /**
422 * Installs flow rules to match ARP reply packets only for VxLAN.
423 *
424 * @param port instance port
425 * @param install installation flag
426 */
427 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
428
429 OpenstackNode localNode = osNodeService.node(port.deviceId());
430
431 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
432 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
433 }
434
435 /**
436 * Installs flow rules to match ARP reply packets only for VLAN.
437 *
438 * @param port instance port
439 * @param install installation flag
440 */
441 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
442
443 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
444 setRemoteArpTreatmentForVlan(selector, port, install);
445 }
446
447 // a helper method
448 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
449 TrafficSelector selector = DefaultTrafficSelector.builder()
450 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
451 .matchArpOp(ARP.OP_REPLY)
452 .matchArpTpa(port.ipAddress().getIp4Address())
453 .matchArpTha(port.macAddress())
454 .build();
455
456 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
457 .setOutput(port.portNumber())
458 .build();
459
460 osFlowRuleService.setRule(
461 appId,
462 port.deviceId(),
463 selector,
464 treatment,
465 PRIORITY_ARP_REPLY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900466 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900467 install
468 );
469
470 return selector;
471 }
472
473 // a helper method
474 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
475 InstancePort port,
476 OpenstackNode localNode,
477 boolean install) {
478 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
479 if (!remoteNode.intgBridge().equals(port.deviceId())) {
480 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
481 .extension(buildExtension(
482 deviceService,
483 remoteNode.intgBridge(),
484 localNode.dataIp().getIp4Address()),
485 remoteNode.intgBridge())
486 .setOutput(remoteNode.tunnelPortNum())
487 .build();
488
489 osFlowRuleService.setRule(
490 appId,
491 remoteNode.intgBridge(),
492 selector,
493 treatmentToRemote,
494 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900495 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900496 install
497 );
498 }
499 }
500 }
501
502 // a helper method
503 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
504 InstancePort port,
505 boolean install) {
506 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
507 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
508 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
509 .setOutput(remoteNode.vlanPortNum())
510 .build();
511
512 osFlowRuleService.setRule(
513 appId,
514 remoteNode.intgBridge(),
515 selector,
516 treatmentToRemote,
517 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900518 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900519 install);
520 }
521 }
522 }
523
524 /**
525 * Extracts properties from the component configuration context.
526 *
527 * @param context the component context
528 */
529 private void readComponentConfiguration(ComponentContext context) {
530 Dictionary<?, ?> properties = context.getProperties();
531
532 String updatedMac = Tools.get(properties, GATEWAY_MAC);
533 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
534 log.info("Configured. Gateway MAC is {}", gatewayMac);
535 }
536
537 /**
Jian Lieae12362018-04-10 18:48:32 +0900538 * An internal packet processor which processes ARP request, and results in
539 * packet-out ARP reply.
540 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700541 private class InternalPacketProcessor implements PacketProcessor {
542
543 @Override
544 public void process(PacketContext context) {
545 if (context.isHandled()) {
546 return;
547 }
548
549 Ethernet ethPacket = context.inPacket().parsed();
550 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
551 return;
552 }
Jian Li32b03622018-11-06 17:54:24 +0900553
554 eventExecutor.execute(() -> processPacketIn(context, ethPacket));
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700555 }
556 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900557
Jian Lieae12362018-04-10 18:48:32 +0900558 /**
559 * An internal network listener which listens to openstack network event,
560 * manages the gateway collection and installs flow rule that handles
561 * ARP request in data plane.
562 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900563 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
564
565 @Override
566 public boolean isRelevant(OpenstackNetworkEvent event) {
567 Subnet osSubnet = event.subnet();
568 if (osSubnet == null) {
569 return false;
570 }
Jian Lieae12362018-04-10 18:48:32 +0900571
Jian Libb4f5412018-04-12 09:48:50 +0900572 Network network = osNetworkService.network(osSubnet.getNetworkId());
573
Jian Lieae12362018-04-10 18:48:32 +0900574 if (network == null) {
575 log.warn("Network is not specified.");
576 return false;
577 } else {
578 if (network.getNetworkType().equals(NetworkType.FLAT)) {
579 return false;
580 }
581 }
582
583 // do not allow to proceed without leadership
584 NodeId leader = leadershipService.getLeader(appId.name());
585 if (!Objects.equals(localNodeId, leader)) {
586 return false;
587 }
588
Hyunsun Moon44aac662017-02-18 02:07:01 +0900589 return !Strings.isNullOrEmpty(osSubnet.getGateway());
590 }
591
592 @Override
593 public void event(OpenstackNetworkEvent event) {
594 switch (event.type()) {
595 case OPENSTACK_SUBNET_CREATED:
596 case OPENSTACK_SUBNET_UPDATED:
Jian Li32b03622018-11-06 17:54:24 +0900597 eventExecutor.execute(() -> {
598 setFakeGatewayArpRule(event.subnet(), true, null);
599 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900600 break;
601 case OPENSTACK_SUBNET_REMOVED:
Jian Li32b03622018-11-06 17:54:24 +0900602 eventExecutor.execute(() -> {
603 setFakeGatewayArpRule(event.subnet(), false, null);
604 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900605 break;
606 case OPENSTACK_NETWORK_CREATED:
607 case OPENSTACK_NETWORK_UPDATED:
608 case OPENSTACK_NETWORK_REMOVED:
609 case OPENSTACK_PORT_CREATED:
610 case OPENSTACK_PORT_UPDATED:
611 case OPENSTACK_PORT_REMOVED:
612 default:
613 // do nothing for the other events
614 break;
615 }
616 }
Jian Lieae12362018-04-10 18:48:32 +0900617 }
618
619 /**
620 * An internal openstack node listener which is used for listening openstack
621 * node activity. As long as a node is in complete state, we will install
622 * default ARP rule to handle ARP request.
623 */
624 private class InternalNodeEventListener implements OpenstackNodeListener {
625
626 @Override
627 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900628
Jian Lieae12362018-04-10 18:48:32 +0900629 // do not allow to proceed without leadership
630 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900631 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900632 }
633
634 @Override
635 public void event(OpenstackNodeEvent event) {
636 OpenstackNode osNode = event.subject();
637 switch (event.type()) {
638 case OPENSTACK_NODE_COMPLETE:
Jian Li32b03622018-11-06 17:54:24 +0900639 eventExecutor.execute(() -> {
640 setDefaultArpRule(osNode, true);
641 setAllArpRules(osNode, true);
642 });
Jian Lieae12362018-04-10 18:48:32 +0900643 break;
644 case OPENSTACK_NODE_INCOMPLETE:
Jian Li32b03622018-11-06 17:54:24 +0900645 eventExecutor.execute(() -> {
646 setDefaultArpRule(osNode, false);
647 setAllArpRules(osNode, false);
648 });
Jian Lieae12362018-04-10 18:48:32 +0900649 break;
Jian Lieae12362018-04-10 18:48:32 +0900650 default:
651 break;
652 }
653 }
654
Jian Lif96685c2018-05-21 14:14:16 +0900655 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900656
657 if (getArpMode() == null) {
658 return;
659 }
660
Jian Li7f70bb72018-07-06 23:35:30 +0900661 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900662 case ARP_PROXY_MODE:
663 setDefaultArpRuleForProxyMode(osNode, install);
664 break;
665 case ARP_BROADCAST_MODE:
666 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900667
668 // we do not add fake gateway ARP rules for FLAT network
669 // ARP packets generated by FLAT typed VM should not be
670 // delegated to switch to handle
671 osNetworkService.subnets().stream().filter(subnet ->
672 osNetworkService.network(subnet.getNetworkId()) != null &&
673 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900674 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900675 .forEach(subnet ->
676 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900677 break;
678 default:
679 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900680 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900681 break;
Jian Lieae12362018-04-10 18:48:32 +0900682 }
683 }
Jian Lif96685c2018-05-21 14:14:16 +0900684
685 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
686 TrafficSelector selector = DefaultTrafficSelector.builder()
687 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
688 .build();
689
690 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
691 .punt()
692 .build();
693
694 osFlowRuleService.setRule(
695 appId,
696 osNode.intgBridge(),
697 selector,
698 treatment,
699 PRIORITY_ARP_CONTROL_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900700 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900701 install
702 );
703 }
704
705 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
706 TrafficSelector selector = DefaultTrafficSelector.builder()
707 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
708 .matchArpOp(ARP.OP_REQUEST)
709 .build();
710
711 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
712 .setOutput(PortNumber.FLOOD)
713 .build();
714
715 osFlowRuleService.setRule(
716 appId,
717 osNode.intgBridge(),
718 selector,
719 treatment,
Jian Li5c09e212018-10-24 18:23:58 +0900720 PRIORITY_ARP_FLOOD_RULE,
721 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900722 install
723 );
724 }
Jian Li7f70bb72018-07-06 23:35:30 +0900725
Jian Li5b66ce02018-07-09 22:43:54 +0900726 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900727 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900728 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900729 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900730 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
731 .forEach(p -> {
732 setArpRequestRule(p, install);
733 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900734 });
735 }
736 }
Jian Lieae12362018-04-10 18:48:32 +0900737 }
738
739 /**
740 * An internal instance port listener which listens the port events generated
741 * from VM. When ARP a host which located in a remote compute node, we specify
742 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
743 * host IP address. When ARP a host which located in a local compute node,
744 * we specify only ARP OP mode as REQUEST.
745 */
746 private class InternalInstancePortListener implements InstancePortListener {
747
748 @Override
749 public boolean isRelevant(InstancePortEvent event) {
750
Jian Li7f70bb72018-07-06 23:35:30 +0900751 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900752 return false;
753 }
754
755 InstancePort instPort = event.subject();
756 return mastershipService.isLocalMaster(instPort.deviceId());
757 }
758
759 @Override
760 public void event(InstancePortEvent event) {
761 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900762 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900763 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Li32b03622018-11-06 17:54:24 +0900764 eventExecutor.execute(() -> {
765 setArpRequestRule(event.subject(), true);
766 setArpReplyRule(event.subject(), true);
767 });
Jian Lieae12362018-04-10 18:48:32 +0900768 break;
769 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li32b03622018-11-06 17:54:24 +0900770 eventExecutor.execute(() -> {
771 setArpRequestRule(event.subject(), false);
772 setArpReplyRule(event.subject(), false);
773 });
Jian Lieae12362018-04-10 18:48:32 +0900774 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900775 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li32b03622018-11-06 17:54:24 +0900776 eventExecutor.execute(() -> {
777 setArpRequestRule(event.subject(), true);
778 setArpReplyRule(event.subject(), true);
779 });
Jian Liec5c32b2018-07-13 14:28:58 +0900780 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900781 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li32b03622018-11-06 17:54:24 +0900782 eventExecutor.execute(() -> {
783 InstancePort revisedInstPort = swapStaleLocation(event.subject());
784 setArpRequestRule(revisedInstPort, false);
785 });
Jian Li24ec59f2018-05-23 19:01:25 +0900786 break;
Jian Lieae12362018-04-10 18:48:32 +0900787 default:
788 break;
789 }
790 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900791 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700792}