blob: e2f8f92994221579c92b9e82598408e2dae6adde [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.onlab.packet.ARP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090020import org.onlab.packet.EthType;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070021import org.onlab.packet.Ethernet;
22import org.onlab.packet.Ip4Address;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.MacAddress;
25import org.onlab.util.Tools;
Hyunsun Moon44aac662017-02-18 02:07:01 +090026import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090027import org.onosproject.cfg.ConfigProperty;
Jian Lieae12362018-04-10 18:48:32 +090028import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.LeadershipService;
30import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
Jian Lieae12362018-04-10 18:48:32 +090033import org.onosproject.mastership.MastershipService;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.device.DeviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.net.flow.DefaultTrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070037import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onosproject.net.flow.TrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070039import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.packet.DefaultOutboundPacket;
41import org.onosproject.net.packet.PacketContext;
42import org.onosproject.net.packet.PacketProcessor;
43import org.onosproject.net.packet.PacketService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.InstancePort;
Jian Lieae12362018-04-10 18:48:32 +090045import org.onosproject.openstacknetworking.api.InstancePortEvent;
46import org.onosproject.openstacknetworking.api.InstancePortListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090047import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Lieae12362018-04-10 18:48:32 +090048import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090049import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
50import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
51import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lieae12362018-04-10 18:48:32 +090052import org.onosproject.openstacknode.api.OpenstackNode;
53import org.onosproject.openstacknode.api.OpenstackNodeEvent;
54import org.onosproject.openstacknode.api.OpenstackNodeListener;
55import org.onosproject.openstacknode.api.OpenstackNodeService;
56import org.openstack4j.model.network.Network;
57import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090058import org.openstack4j.model.network.Subnet;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070059import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070060import org.osgi.service.component.annotations.Activate;
61import org.osgi.service.component.annotations.Component;
62import org.osgi.service.component.annotations.Deactivate;
63import org.osgi.service.component.annotations.Modified;
64import org.osgi.service.component.annotations.Reference;
65import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070066import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
Hyunsun Moon44aac662017-02-18 02:07:01 +090068
Hyunsun Moonb974fca2016-06-30 21:20:39 -070069import java.nio.ByteBuffer;
70import java.util.Dictionary;
Jian Lieae12362018-04-10 18:48:32 +090071import java.util.Objects;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070072import java.util.Set;
73
74import static com.google.common.base.Preconditions.checkNotNull;
Jian Lieae12362018-04-10 18:48:32 +090075import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
76import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
Jian Li5c09e212018-10-24 18:23:58 +090077import static org.onosproject.openstacknetworking.api.Constants.ARP_TABLE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090078import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lieae12362018-04-10 18:48:32 +090079import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li5c09e212018-10-24 18:23:58 +090080import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_FLOOD_RULE;
Jian Lieae12362018-04-10 18:48:32 +090081import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
82import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
83import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
Jian Lic2403592018-07-18 12:56:45 +090084import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Ray Milkey8e406512018-10-24 15:56:50 -070085import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE;
86import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
87import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
88import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
Jian Li7f70bb72018-07-06 23:35:30 +090089import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090090import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090091import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
92import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070093
94/**
95 * Handles ARP packet from VMs.
96 */
Ray Milkey8e406512018-10-24 15:56:50 -070097@Component(
98 immediate = true,
99 property = {
100 GATEWAY_MAC + "=" + GATEWAY_MAC_DEFAULT,
101 ARP_MODE + "=" + ARP_MODE_DEFAULT,
102 }
103)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700105
106 private final Logger log = LoggerFactory.getLogger(getClass());
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800109 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800112 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900115 OpenstackFlowRuleService osFlowRuleService;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800118 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900121 ClusterService clusterService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900124 LeadershipService leadershipService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900127 DeviceService deviceService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900130 MastershipService mastershipService;
131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800133 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800136 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900139 protected OpenstackNodeService osNodeService;
140
Ray Milkey8e406512018-10-24 15:56:50 -0700141 /** Fake MAC address for virtual network subnet gateway. */
142 private String gatewayMac = GATEWAY_MAC_DEFAULT;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700143
Ray Milkey8e406512018-10-24 15:56:50 -0700144 /** ARP processing mode, broadcast | proxy (default). */
145 protected String arpMode = ARP_MODE_DEFAULT;
Jian Lieae12362018-04-10 18:48:32 +0900146
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700147 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900148 private final InternalOpenstackNetworkListener osNetworkListener =
149 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900150 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
151 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
152
Hyunsun Moon44aac662017-02-18 02:07:01 +0900153
154 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900155 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700156
157 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800158 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900159 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
160 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900161 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900162 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900163 osNodeService.addListener(osNodeListener);
164 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700165 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900166
167 instancePortService.addListener(instancePortListener);
168
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700170 }
171
172 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800173 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700174 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900176 osNodeService.removeListener(osNodeListener);
177 instancePortService.removeListener(instancePortListener);
178 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900179 configService.unregisterProperties(getClass(), false);
180
181 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700182 }
183
184 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800185 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900186 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900187
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700188 log.info("Modified");
189 }
190
Jian Li7f70bb72018-07-06 23:35:30 +0900191 private String getArpMode() {
192 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
193 return getPropertyValue(properties, ARP_MODE);
194 }
195
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700196 /**
197 * Processes ARP request packets.
198 * It checks if the target IP is owned by a known host first and then ask to
199 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
200 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900201 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700202 * @param ethPacket ethernet packet
203 */
204 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900205
206 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900207 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900208 return;
209 }
210
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700211 ARP arpPacket = (ARP) ethPacket.getPayload();
212 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
213 return;
214 }
215
Hyunsun Moon44aac662017-02-18 02:07:01 +0900216 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
217 if (srcInstPort == null) {
218 log.trace("Failed to find source instance port(MAC:{})",
219 ethPacket.getSourceMAC());
220 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700221 }
222
Hyunsun Moon44aac662017-02-18 02:07:01 +0900223 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Daniel Park4d7f88b2018-09-19 19:03:38 +0900224
Jian Liac30e272018-10-18 23:08:03 +0900225 MacAddress replyMac = isGatewayIp(targetIp) ? MacAddress.valueOf(gatewayMac) :
Hyunsun Moon44aac662017-02-18 02:07:01 +0900226 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700227 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900228 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700229 return;
230 }
231
232 Ethernet ethReply = ARP.buildArpReply(
233 targetIp.getIp4Address(),
234 replyMac,
235 ethPacket);
236
237 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
238 .setOutput(context.inPacket().receivedFrom().port())
239 .build();
240
241 packetService.emit(new DefaultOutboundPacket(
242 context.inPacket().receivedFrom().deviceId(),
243 treatment,
244 ByteBuffer.wrap(ethReply.serialize())));
245 }
246
Jian Liac30e272018-10-18 23:08:03 +0900247 /**
248 * Denotes whether the given target IP is gateway IP.
249 *
250 * @param targetIp target IP address
251 * @return true if the given targetIP is gateway IP, false otherwise.
252 */
253 private boolean isGatewayIp(IpAddress targetIp) {
Daniel Park4d7f88b2018-09-19 19:03:38 +0900254 return osNetworkService.subnets().stream()
Jian Liac30e272018-10-18 23:08:03 +0900255 .filter(Objects::nonNull)
256 .filter(subnet -> subnet.getGateway() != null)
Daniel Park4d7f88b2018-09-19 19:03:38 +0900257 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
258 }
259
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700260 /**
261 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900262 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700263 *
264 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900265 * @param osNetId openstack network id of the source instance port
266 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700267 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900268 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700269 checkNotNull(targetIp);
270
Hyunsun Moon44aac662017-02-18 02:07:01 +0900271 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
272 if (instPort != null) {
273 log.trace("Found MAC from host service for {}", targetIp);
274 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700275 } else {
276 return MacAddress.NONE;
277 }
278 }
279
Jian Lieae12362018-04-10 18:48:32 +0900280 /**
Daniel Park613ac372018-06-28 14:30:11 +0900281 * Installs flow rules which convert ARP request packet into ARP reply
282 * by adding a fake gateway MAC address as Source Hardware Address.
283 *
284 * @param osSubnet openstack subnet
285 * @param install flag which indicates whether to install rule or remove rule
286 */
287 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
288
Jian Li7f70bb72018-07-06 23:35:30 +0900289 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li8e365bd2018-10-12 22:09:03 +0900290
291 // do not remove fake gateway ARP rules, if there is another gateway
292 // which has the same subnet that to be removed
293 // this only occurs if we have duplicated subnets associated with
294 // different networks
295 if (!install) {
296 long numOfDupGws = osNetworkService.subnets().stream()
297 .filter(s -> !s.getId().equals(osSubnet.getId()))
Jian Li7ce775a2018-10-18 07:24:53 +0900298 .filter(s -> s.getGateway() != null)
Jian Li8e365bd2018-10-12 22:09:03 +0900299 .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
300 .count();
301 if (numOfDupGws > 0) {
302 return;
303 }
304 }
305
Daniel Park613ac372018-06-28 14:30:11 +0900306 String gateway = osSubnet.getGateway();
307
308 TrafficSelector selector = DefaultTrafficSelector.builder()
309 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
310 .matchArpOp(ARP.OP_REQUEST)
311 .matchArpTpa(Ip4Address.valueOf(gateway))
312 .build();
313
314 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
315 .setArpOp(ARP.OP_REPLY)
316 .setArpSha(MacAddress.valueOf(gatewayMac))
317 .setArpSpa(Ip4Address.valueOf(gateway))
318 .setOutput(PortNumber.IN_PORT)
319 .build();
320
321 if (osNode == null) {
322 osNodeService.completeNodes(COMPUTE).forEach(n ->
323 osFlowRuleService.setRule(
324 appId,
325 n.intgBridge(),
326 selector,
327 treatment,
328 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900329 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900330 install
331 )
332 );
333 } else {
334 osFlowRuleService.setRule(
335 appId,
336 osNode.intgBridge(),
337 selector,
338 treatment,
339 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900340 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900341 install
342 );
343 }
344
345 }
346 }
347
348 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900349 * Installs flow rules to match ARP request packets.
350 *
351 * @param port instance port
352 * @param install installation flag
353 */
354 private void setArpRequestRule(InstancePort port, boolean install) {
355 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
356
357 switch (type) {
358 case VXLAN:
359 setRemoteArpRequestRuleForVxlan(port, install);
360 break;
361 case VLAN:
362 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
363 // there is no need to add any flow rules to handle ARP request
364 break;
365 default:
366 break;
367 }
368 }
369
370 /**
371 * Installs flow rules to match ARP reply packets.
372 *
373 * @param port instance port
374 * @param install installation flag
375 */
376 private void setArpReplyRule(InstancePort port, boolean install) {
377 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
378
379 switch (type) {
380 case VXLAN:
381 setArpReplyRuleForVxlan(port, install);
382 break;
383 case VLAN:
384 setArpReplyRuleForVlan(port, install);
385 break;
386 default:
387 break;
388 }
389 }
390
391 /**
392 * Installs flow rules to match ARP request packets only for VxLAN.
393 *
394 * @param port instance port
395 * @param install installation flag
396 */
397 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
398
399 OpenstackNode localNode = osNodeService.node(port.deviceId());
400
Jian Li5c09e212018-10-24 18:23:58 +0900401 String segId = osNetworkService.segmentId(port.networkId());
402
Jian Li7f70bb72018-07-06 23:35:30 +0900403 TrafficSelector selector = DefaultTrafficSelector.builder()
404 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
405 .matchArpOp(ARP.OP_REQUEST)
406 .matchArpTpa(port.ipAddress().getIp4Address())
Jian Li5c09e212018-10-24 18:23:58 +0900407 .matchTunnelId(Long.valueOf(segId))
Jian Li7f70bb72018-07-06 23:35:30 +0900408 .build();
409
410 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
411 }
412
413 /**
414 * Installs flow rules to match ARP reply packets only for VxLAN.
415 *
416 * @param port instance port
417 * @param install installation flag
418 */
419 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
420
421 OpenstackNode localNode = osNodeService.node(port.deviceId());
422
423 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
424 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
425 }
426
427 /**
428 * Installs flow rules to match ARP reply packets only for VLAN.
429 *
430 * @param port instance port
431 * @param install installation flag
432 */
433 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
434
435 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
436 setRemoteArpTreatmentForVlan(selector, port, install);
437 }
438
439 // a helper method
440 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
441 TrafficSelector selector = DefaultTrafficSelector.builder()
442 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
443 .matchArpOp(ARP.OP_REPLY)
444 .matchArpTpa(port.ipAddress().getIp4Address())
445 .matchArpTha(port.macAddress())
446 .build();
447
448 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
449 .setOutput(port.portNumber())
450 .build();
451
452 osFlowRuleService.setRule(
453 appId,
454 port.deviceId(),
455 selector,
456 treatment,
457 PRIORITY_ARP_REPLY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900458 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900459 install
460 );
461
462 return selector;
463 }
464
465 // a helper method
466 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
467 InstancePort port,
468 OpenstackNode localNode,
469 boolean install) {
470 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
471 if (!remoteNode.intgBridge().equals(port.deviceId())) {
472 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
473 .extension(buildExtension(
474 deviceService,
475 remoteNode.intgBridge(),
476 localNode.dataIp().getIp4Address()),
477 remoteNode.intgBridge())
478 .setOutput(remoteNode.tunnelPortNum())
479 .build();
480
481 osFlowRuleService.setRule(
482 appId,
483 remoteNode.intgBridge(),
484 selector,
485 treatmentToRemote,
486 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900487 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900488 install
489 );
490 }
491 }
492 }
493
494 // a helper method
495 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
496 InstancePort port,
497 boolean install) {
498 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
499 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
500 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
501 .setOutput(remoteNode.vlanPortNum())
502 .build();
503
504 osFlowRuleService.setRule(
505 appId,
506 remoteNode.intgBridge(),
507 selector,
508 treatmentToRemote,
509 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900510 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900511 install);
512 }
513 }
514 }
515
516 /**
517 * Extracts properties from the component configuration context.
518 *
519 * @param context the component context
520 */
521 private void readComponentConfiguration(ComponentContext context) {
522 Dictionary<?, ?> properties = context.getProperties();
523
524 String updatedMac = Tools.get(properties, GATEWAY_MAC);
Ray Milkey8e406512018-10-24 15:56:50 -0700525 gatewayMac = updatedMac != null ? updatedMac : GATEWAY_MAC_DEFAULT;
Jian Li7f70bb72018-07-06 23:35:30 +0900526 log.info("Configured. Gateway MAC is {}", gatewayMac);
527 }
528
529 /**
Jian Lieae12362018-04-10 18:48:32 +0900530 * An internal packet processor which processes ARP request, and results in
531 * packet-out ARP reply.
532 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700533 private class InternalPacketProcessor implements PacketProcessor {
534
535 @Override
536 public void process(PacketContext context) {
537 if (context.isHandled()) {
538 return;
539 }
540
541 Ethernet ethPacket = context.inPacket().parsed();
542 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
543 return;
544 }
545 processPacketIn(context, ethPacket);
546 }
547 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900548
Jian Lieae12362018-04-10 18:48:32 +0900549 /**
550 * An internal network listener which listens to openstack network event,
551 * manages the gateway collection and installs flow rule that handles
552 * ARP request in data plane.
553 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900554 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
555
556 @Override
557 public boolean isRelevant(OpenstackNetworkEvent event) {
558 Subnet osSubnet = event.subnet();
559 if (osSubnet == null) {
560 return false;
561 }
Jian Lieae12362018-04-10 18:48:32 +0900562
Jian Libb4f5412018-04-12 09:48:50 +0900563 Network network = osNetworkService.network(osSubnet.getNetworkId());
564
Jian Lieae12362018-04-10 18:48:32 +0900565 if (network == null) {
566 log.warn("Network is not specified.");
567 return false;
568 } else {
569 if (network.getNetworkType().equals(NetworkType.FLAT)) {
570 return false;
571 }
572 }
573
574 // do not allow to proceed without leadership
575 NodeId leader = leadershipService.getLeader(appId.name());
576 if (!Objects.equals(localNodeId, leader)) {
577 return false;
578 }
579
Hyunsun Moon44aac662017-02-18 02:07:01 +0900580 return !Strings.isNullOrEmpty(osSubnet.getGateway());
581 }
582
583 @Override
584 public void event(OpenstackNetworkEvent event) {
585 switch (event.type()) {
586 case OPENSTACK_SUBNET_CREATED:
587 case OPENSTACK_SUBNET_UPDATED:
Daniel Park613ac372018-06-28 14:30:11 +0900588 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900589 break;
590 case OPENSTACK_SUBNET_REMOVED:
Daniel Park613ac372018-06-28 14:30:11 +0900591 setFakeGatewayArpRule(event.subnet(), false, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900592 break;
593 case OPENSTACK_NETWORK_CREATED:
594 case OPENSTACK_NETWORK_UPDATED:
595 case OPENSTACK_NETWORK_REMOVED:
596 case OPENSTACK_PORT_CREATED:
597 case OPENSTACK_PORT_UPDATED:
598 case OPENSTACK_PORT_REMOVED:
599 default:
600 // do nothing for the other events
601 break;
602 }
603 }
Jian Lieae12362018-04-10 18:48:32 +0900604 }
605
606 /**
607 * An internal openstack node listener which is used for listening openstack
608 * node activity. As long as a node is in complete state, we will install
609 * default ARP rule to handle ARP request.
610 */
611 private class InternalNodeEventListener implements OpenstackNodeListener {
612
613 @Override
614 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900615
Jian Lieae12362018-04-10 18:48:32 +0900616 // do not allow to proceed without leadership
617 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900618 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900619 }
620
621 @Override
622 public void event(OpenstackNodeEvent event) {
623 OpenstackNode osNode = event.subject();
624 switch (event.type()) {
625 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900626 setDefaultArpRule(osNode, true);
Jian Li5b66ce02018-07-09 22:43:54 +0900627 setAllArpRules(osNode, true);
Jian Lieae12362018-04-10 18:48:32 +0900628 break;
629 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900630 setDefaultArpRule(osNode, false);
Jian Li5b66ce02018-07-09 22:43:54 +0900631 setAllArpRules(osNode, false);
Jian Lieae12362018-04-10 18:48:32 +0900632 break;
Jian Lieae12362018-04-10 18:48:32 +0900633 default:
634 break;
635 }
636 }
637
Jian Lif96685c2018-05-21 14:14:16 +0900638 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900639
640 if (getArpMode() == null) {
641 return;
642 }
643
Jian Li7f70bb72018-07-06 23:35:30 +0900644 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900645 case ARP_PROXY_MODE:
646 setDefaultArpRuleForProxyMode(osNode, install);
647 break;
648 case ARP_BROADCAST_MODE:
649 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900650
651 // we do not add fake gateway ARP rules for FLAT network
652 // ARP packets generated by FLAT typed VM should not be
653 // delegated to switch to handle
654 osNetworkService.subnets().stream().filter(subnet ->
655 osNetworkService.network(subnet.getNetworkId()) != null &&
656 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900657 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900658 .forEach(subnet ->
659 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900660 break;
661 default:
662 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900663 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900664 break;
Jian Lieae12362018-04-10 18:48:32 +0900665 }
666 }
Jian Lif96685c2018-05-21 14:14:16 +0900667
668 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
669 TrafficSelector selector = DefaultTrafficSelector.builder()
670 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
671 .build();
672
673 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
674 .punt()
675 .build();
676
677 osFlowRuleService.setRule(
678 appId,
679 osNode.intgBridge(),
680 selector,
681 treatment,
682 PRIORITY_ARP_CONTROL_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900683 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900684 install
685 );
686 }
687
688 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
689 TrafficSelector selector = DefaultTrafficSelector.builder()
690 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
691 .matchArpOp(ARP.OP_REQUEST)
692 .build();
693
694 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
695 .setOutput(PortNumber.FLOOD)
696 .build();
697
698 osFlowRuleService.setRule(
699 appId,
700 osNode.intgBridge(),
701 selector,
702 treatment,
Jian Li5c09e212018-10-24 18:23:58 +0900703 PRIORITY_ARP_FLOOD_RULE,
704 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900705 install
706 );
707 }
Jian Li7f70bb72018-07-06 23:35:30 +0900708
Jian Li5b66ce02018-07-09 22:43:54 +0900709 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900710 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900711 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900712 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900713 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
714 .forEach(p -> {
715 setArpRequestRule(p, install);
716 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900717 });
718 }
719 }
Jian Lieae12362018-04-10 18:48:32 +0900720 }
721
722 /**
723 * An internal instance port listener which listens the port events generated
724 * from VM. When ARP a host which located in a remote compute node, we specify
725 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
726 * host IP address. When ARP a host which located in a local compute node,
727 * we specify only ARP OP mode as REQUEST.
728 */
729 private class InternalInstancePortListener implements InstancePortListener {
730
731 @Override
732 public boolean isRelevant(InstancePortEvent event) {
733
Jian Li7f70bb72018-07-06 23:35:30 +0900734 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900735 return false;
736 }
737
738 InstancePort instPort = event.subject();
739 return mastershipService.isLocalMaster(instPort.deviceId());
740 }
741
742 @Override
743 public void event(InstancePortEvent event) {
744 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900745 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900746 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lieae12362018-04-10 18:48:32 +0900747 setArpRequestRule(event.subject(), true);
748 setArpReplyRule(event.subject(), true);
749 break;
750 case OPENSTACK_INSTANCE_PORT_VANISHED:
751 setArpRequestRule(event.subject(), false);
752 setArpReplyRule(event.subject(), false);
753 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900754 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
755 setArpRequestRule(event.subject(), true);
756 setArpReplyRule(event.subject(), true);
757 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900758 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900759 InstancePort revisedInstPort = swapStaleLocation(event.subject());
760 setArpRequestRule(revisedInstPort, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900761 break;
Jian Lieae12362018-04-10 18:48:32 +0900762 default:
763 break;
764 }
765 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900766 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700767}