blob: a4696d585330ee459b7022bd168f734972569211 [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;
Jian Li32b03622018-11-06 17:54:24 +090073import java.util.concurrent.ExecutorService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070074
75import static com.google.common.base.Preconditions.checkNotNull;
Jian Li32b03622018-11-06 17:54:24 +090076import static java.util.concurrent.Executors.newSingleThreadExecutor;
77import static org.onlab.util.Tools.groupedThreads;
Jian Lieae12362018-04-10 18:48:32 +090078import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
79import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
Jian Li5c09e212018-10-24 18:23:58 +090080import static org.onosproject.openstacknetworking.api.Constants.ARP_TABLE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090081import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lieae12362018-04-10 18:48:32 +090082import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li5c09e212018-10-24 18:23:58 +090083import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_FLOOD_RULE;
Jian Lieae12362018-04-10 18:48:32 +090084import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
85import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
86import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
Jian Lic2403592018-07-18 12:56:45 +090087import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Ray Milkey8e406512018-10-24 15:56:50 -070088import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE;
89import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
90import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
91import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
Jian Li7f70bb72018-07-06 23:35:30 +090092import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090093import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090094import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
95import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070096
97/**
98 * Handles ARP packet from VMs.
99 */
Ray Milkey8e406512018-10-24 15:56:50 -0700100@Component(
101 immediate = true,
102 property = {
103 GATEWAY_MAC + "=" + GATEWAY_MAC_DEFAULT,
104 ARP_MODE + "=" + ARP_MODE_DEFAULT,
105 }
106)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700108
109 private final Logger log = LoggerFactory.getLogger(getClass());
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800112 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800115 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900118 OpenstackFlowRuleService osFlowRuleService;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800121 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900124 ClusterService clusterService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900127 LeadershipService leadershipService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900130 DeviceService deviceService;
131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900133 MastershipService mastershipService;
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800136 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800139 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900142 protected OpenstackNodeService osNodeService;
143
Ray Milkey8e406512018-10-24 15:56:50 -0700144 /** Fake MAC address for virtual network subnet gateway. */
145 private String gatewayMac = GATEWAY_MAC_DEFAULT;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700146
Ray Milkey8e406512018-10-24 15:56:50 -0700147 /** ARP processing mode, broadcast | proxy (default). */
148 protected String arpMode = ARP_MODE_DEFAULT;
Jian Lieae12362018-04-10 18:48:32 +0900149
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700150 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900151 private final InternalOpenstackNetworkListener osNetworkListener =
152 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900153 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
154 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
155
Jian Li32b03622018-11-06 17:54:24 +0900156 private final ExecutorService eventExecutor = newSingleThreadExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
158
Hyunsun Moon44aac662017-02-18 02:07:01 +0900159
160 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900161 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700162
163 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800164 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900165 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
166 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900167 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900168 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900169 osNodeService.addListener(osNodeListener);
170 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700171 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900172
173 instancePortService.addListener(instancePortListener);
174
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700176 }
177
178 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800179 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700180 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900182 osNodeService.removeListener(osNodeListener);
183 instancePortService.removeListener(instancePortListener);
184 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900185 configService.unregisterProperties(getClass(), false);
Jian Li32b03622018-11-06 17:54:24 +0900186 eventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900187
188 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700189 }
190
191 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800192 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900193 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900194
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700195 log.info("Modified");
196 }
197
Jian Li7f70bb72018-07-06 23:35:30 +0900198 private String getArpMode() {
199 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
200 return getPropertyValue(properties, ARP_MODE);
201 }
202
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700203 /**
204 * Processes ARP request packets.
205 * It checks if the target IP is owned by a known host first and then ask to
206 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
207 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900208 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700209 * @param ethPacket ethernet packet
210 */
211 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900212
213 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900214 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900215 return;
216 }
217
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700218 ARP arpPacket = (ARP) ethPacket.getPayload();
219 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
220 return;
221 }
222
Hyunsun Moon44aac662017-02-18 02:07:01 +0900223 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
224 if (srcInstPort == null) {
225 log.trace("Failed to find source instance port(MAC:{})",
226 ethPacket.getSourceMAC());
227 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700228 }
229
Hyunsun Moon44aac662017-02-18 02:07:01 +0900230 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Daniel Park4d7f88b2018-09-19 19:03:38 +0900231
Jian Liac30e272018-10-18 23:08:03 +0900232 MacAddress replyMac = isGatewayIp(targetIp) ? MacAddress.valueOf(gatewayMac) :
Hyunsun Moon44aac662017-02-18 02:07:01 +0900233 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700234 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900235 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700236 return;
237 }
238
239 Ethernet ethReply = ARP.buildArpReply(
240 targetIp.getIp4Address(),
241 replyMac,
242 ethPacket);
243
244 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
245 .setOutput(context.inPacket().receivedFrom().port())
246 .build();
247
248 packetService.emit(new DefaultOutboundPacket(
249 context.inPacket().receivedFrom().deviceId(),
250 treatment,
251 ByteBuffer.wrap(ethReply.serialize())));
252 }
253
Jian Liac30e272018-10-18 23:08:03 +0900254 /**
255 * Denotes whether the given target IP is gateway IP.
256 *
257 * @param targetIp target IP address
258 * @return true if the given targetIP is gateway IP, false otherwise.
259 */
260 private boolean isGatewayIp(IpAddress targetIp) {
Daniel Park4d7f88b2018-09-19 19:03:38 +0900261 return osNetworkService.subnets().stream()
Jian Liac30e272018-10-18 23:08:03 +0900262 .filter(Objects::nonNull)
263 .filter(subnet -> subnet.getGateway() != null)
Daniel Park4d7f88b2018-09-19 19:03:38 +0900264 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
265 }
266
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700267 /**
268 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900269 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700270 *
271 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900272 * @param osNetId openstack network id of the source instance port
273 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700274 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900275 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700276 checkNotNull(targetIp);
277
Hyunsun Moon44aac662017-02-18 02:07:01 +0900278 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
279 if (instPort != null) {
280 log.trace("Found MAC from host service for {}", targetIp);
281 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700282 } else {
283 return MacAddress.NONE;
284 }
285 }
286
Jian Lieae12362018-04-10 18:48:32 +0900287 /**
Daniel Park613ac372018-06-28 14:30:11 +0900288 * Installs flow rules which convert ARP request packet into ARP reply
289 * by adding a fake gateway MAC address as Source Hardware Address.
290 *
291 * @param osSubnet openstack subnet
292 * @param install flag which indicates whether to install rule or remove rule
293 */
294 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
295
Jian Li7f70bb72018-07-06 23:35:30 +0900296 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li8e365bd2018-10-12 22:09:03 +0900297
298 // do not remove fake gateway ARP rules, if there is another gateway
299 // which has the same subnet that to be removed
300 // this only occurs if we have duplicated subnets associated with
301 // different networks
302 if (!install) {
303 long numOfDupGws = osNetworkService.subnets().stream()
304 .filter(s -> !s.getId().equals(osSubnet.getId()))
Jian Li7ce775a2018-10-18 07:24:53 +0900305 .filter(s -> s.getGateway() != null)
Jian Li8e365bd2018-10-12 22:09:03 +0900306 .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
307 .count();
308 if (numOfDupGws > 0) {
309 return;
310 }
311 }
312
Daniel Park613ac372018-06-28 14:30:11 +0900313 String gateway = osSubnet.getGateway();
Daniel Park6a2d95e2018-11-05 18:50:16 +0900314 if (gateway == null) {
315 return;
316 }
Daniel Park613ac372018-06-28 14:30:11 +0900317
318 TrafficSelector selector = DefaultTrafficSelector.builder()
319 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
320 .matchArpOp(ARP.OP_REQUEST)
321 .matchArpTpa(Ip4Address.valueOf(gateway))
322 .build();
323
324 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
325 .setArpOp(ARP.OP_REPLY)
326 .setArpSha(MacAddress.valueOf(gatewayMac))
327 .setArpSpa(Ip4Address.valueOf(gateway))
328 .setOutput(PortNumber.IN_PORT)
329 .build();
330
331 if (osNode == null) {
332 osNodeService.completeNodes(COMPUTE).forEach(n ->
333 osFlowRuleService.setRule(
334 appId,
335 n.intgBridge(),
336 selector,
337 treatment,
338 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900339 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900340 install
341 )
342 );
343 } else {
344 osFlowRuleService.setRule(
345 appId,
346 osNode.intgBridge(),
347 selector,
348 treatment,
349 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900350 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900351 install
352 );
353 }
354
355 }
356 }
357
358 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900359 * Installs flow rules to match ARP request packets.
360 *
361 * @param port instance port
362 * @param install installation flag
363 */
364 private void setArpRequestRule(InstancePort port, boolean install) {
365 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
366
367 switch (type) {
368 case VXLAN:
369 setRemoteArpRequestRuleForVxlan(port, install);
370 break;
371 case VLAN:
372 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
373 // there is no need to add any flow rules to handle ARP request
374 break;
375 default:
376 break;
377 }
378 }
379
380 /**
381 * Installs flow rules to match ARP reply packets.
382 *
383 * @param port instance port
384 * @param install installation flag
385 */
386 private void setArpReplyRule(InstancePort port, boolean install) {
387 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
388
389 switch (type) {
390 case VXLAN:
391 setArpReplyRuleForVxlan(port, install);
392 break;
393 case VLAN:
394 setArpReplyRuleForVlan(port, install);
395 break;
396 default:
397 break;
398 }
399 }
400
401 /**
402 * Installs flow rules to match ARP request packets only for VxLAN.
403 *
404 * @param port instance port
405 * @param install installation flag
406 */
407 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
408
409 OpenstackNode localNode = osNodeService.node(port.deviceId());
410
Jian Li5c09e212018-10-24 18:23:58 +0900411 String segId = osNetworkService.segmentId(port.networkId());
412
Jian Li7f70bb72018-07-06 23:35:30 +0900413 TrafficSelector selector = DefaultTrafficSelector.builder()
414 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
415 .matchArpOp(ARP.OP_REQUEST)
416 .matchArpTpa(port.ipAddress().getIp4Address())
Jian Li5c09e212018-10-24 18:23:58 +0900417 .matchTunnelId(Long.valueOf(segId))
Jian Li7f70bb72018-07-06 23:35:30 +0900418 .build();
419
420 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
421 }
422
423 /**
424 * Installs flow rules to match ARP reply packets only for VxLAN.
425 *
426 * @param port instance port
427 * @param install installation flag
428 */
429 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
430
431 OpenstackNode localNode = osNodeService.node(port.deviceId());
432
433 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
434 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
435 }
436
437 /**
438 * Installs flow rules to match ARP reply packets only for VLAN.
439 *
440 * @param port instance port
441 * @param install installation flag
442 */
443 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
444
445 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
446 setRemoteArpTreatmentForVlan(selector, port, install);
447 }
448
449 // a helper method
450 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
451 TrafficSelector selector = DefaultTrafficSelector.builder()
452 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
453 .matchArpOp(ARP.OP_REPLY)
454 .matchArpTpa(port.ipAddress().getIp4Address())
455 .matchArpTha(port.macAddress())
456 .build();
457
458 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
459 .setOutput(port.portNumber())
460 .build();
461
462 osFlowRuleService.setRule(
463 appId,
464 port.deviceId(),
465 selector,
466 treatment,
467 PRIORITY_ARP_REPLY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900468 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900469 install
470 );
471
472 return selector;
473 }
474
475 // a helper method
476 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
477 InstancePort port,
478 OpenstackNode localNode,
479 boolean install) {
480 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
481 if (!remoteNode.intgBridge().equals(port.deviceId())) {
482 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
483 .extension(buildExtension(
484 deviceService,
485 remoteNode.intgBridge(),
486 localNode.dataIp().getIp4Address()),
487 remoteNode.intgBridge())
488 .setOutput(remoteNode.tunnelPortNum())
489 .build();
490
491 osFlowRuleService.setRule(
492 appId,
493 remoteNode.intgBridge(),
494 selector,
495 treatmentToRemote,
496 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900497 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900498 install
499 );
500 }
501 }
502 }
503
504 // a helper method
505 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
506 InstancePort port,
507 boolean install) {
508 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
509 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
510 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
511 .setOutput(remoteNode.vlanPortNum())
512 .build();
513
514 osFlowRuleService.setRule(
515 appId,
516 remoteNode.intgBridge(),
517 selector,
518 treatmentToRemote,
519 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900520 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900521 install);
522 }
523 }
524 }
525
526 /**
527 * Extracts properties from the component configuration context.
528 *
529 * @param context the component context
530 */
531 private void readComponentConfiguration(ComponentContext context) {
532 Dictionary<?, ?> properties = context.getProperties();
533
534 String updatedMac = Tools.get(properties, GATEWAY_MAC);
Ray Milkey8e406512018-10-24 15:56:50 -0700535 gatewayMac = updatedMac != null ? updatedMac : GATEWAY_MAC_DEFAULT;
Jian Li7f70bb72018-07-06 23:35:30 +0900536 log.info("Configured. Gateway MAC is {}", gatewayMac);
537 }
538
539 /**
Jian Lieae12362018-04-10 18:48:32 +0900540 * An internal packet processor which processes ARP request, and results in
541 * packet-out ARP reply.
542 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700543 private class InternalPacketProcessor implements PacketProcessor {
544
545 @Override
546 public void process(PacketContext context) {
547 if (context.isHandled()) {
548 return;
549 }
550
551 Ethernet ethPacket = context.inPacket().parsed();
552 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
553 return;
554 }
Jian Li32b03622018-11-06 17:54:24 +0900555
556 eventExecutor.execute(() -> processPacketIn(context, ethPacket));
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700557 }
558 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900559
Jian Lieae12362018-04-10 18:48:32 +0900560 /**
561 * An internal network listener which listens to openstack network event,
562 * manages the gateway collection and installs flow rule that handles
563 * ARP request in data plane.
564 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900565 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
566
567 @Override
568 public boolean isRelevant(OpenstackNetworkEvent event) {
569 Subnet osSubnet = event.subnet();
570 if (osSubnet == null) {
571 return false;
572 }
Jian Lieae12362018-04-10 18:48:32 +0900573
Jian Libb4f5412018-04-12 09:48:50 +0900574 Network network = osNetworkService.network(osSubnet.getNetworkId());
575
Jian Lieae12362018-04-10 18:48:32 +0900576 if (network == null) {
577 log.warn("Network is not specified.");
578 return false;
579 } else {
580 if (network.getNetworkType().equals(NetworkType.FLAT)) {
581 return false;
582 }
583 }
584
585 // do not allow to proceed without leadership
586 NodeId leader = leadershipService.getLeader(appId.name());
587 if (!Objects.equals(localNodeId, leader)) {
588 return false;
589 }
590
Hyunsun Moon44aac662017-02-18 02:07:01 +0900591 return !Strings.isNullOrEmpty(osSubnet.getGateway());
592 }
593
594 @Override
595 public void event(OpenstackNetworkEvent event) {
596 switch (event.type()) {
597 case OPENSTACK_SUBNET_CREATED:
598 case OPENSTACK_SUBNET_UPDATED:
Jian Li32b03622018-11-06 17:54:24 +0900599 eventExecutor.execute(() -> {
600 setFakeGatewayArpRule(event.subnet(), true, null);
601 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900602 break;
603 case OPENSTACK_SUBNET_REMOVED:
Jian Li32b03622018-11-06 17:54:24 +0900604 eventExecutor.execute(() -> {
605 setFakeGatewayArpRule(event.subnet(), false, null);
606 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900607 break;
608 case OPENSTACK_NETWORK_CREATED:
609 case OPENSTACK_NETWORK_UPDATED:
610 case OPENSTACK_NETWORK_REMOVED:
611 case OPENSTACK_PORT_CREATED:
612 case OPENSTACK_PORT_UPDATED:
613 case OPENSTACK_PORT_REMOVED:
614 default:
615 // do nothing for the other events
616 break;
617 }
618 }
Jian Lieae12362018-04-10 18:48:32 +0900619 }
620
621 /**
622 * An internal openstack node listener which is used for listening openstack
623 * node activity. As long as a node is in complete state, we will install
624 * default ARP rule to handle ARP request.
625 */
626 private class InternalNodeEventListener implements OpenstackNodeListener {
627
628 @Override
629 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900630
Jian Lieae12362018-04-10 18:48:32 +0900631 // do not allow to proceed without leadership
632 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900633 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900634 }
635
636 @Override
637 public void event(OpenstackNodeEvent event) {
638 OpenstackNode osNode = event.subject();
639 switch (event.type()) {
640 case OPENSTACK_NODE_COMPLETE:
Jian Li32b03622018-11-06 17:54:24 +0900641 eventExecutor.execute(() -> {
642 setDefaultArpRule(osNode, true);
643 setAllArpRules(osNode, true);
644 });
Jian Lieae12362018-04-10 18:48:32 +0900645 break;
646 case OPENSTACK_NODE_INCOMPLETE:
Jian Li32b03622018-11-06 17:54:24 +0900647 eventExecutor.execute(() -> {
648 setDefaultArpRule(osNode, false);
649 setAllArpRules(osNode, false);
650 });
Jian Lieae12362018-04-10 18:48:32 +0900651 break;
Jian Lieae12362018-04-10 18:48:32 +0900652 default:
653 break;
654 }
655 }
656
Jian Lif96685c2018-05-21 14:14:16 +0900657 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900658
659 if (getArpMode() == null) {
660 return;
661 }
662
Jian Li7f70bb72018-07-06 23:35:30 +0900663 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900664 case ARP_PROXY_MODE:
665 setDefaultArpRuleForProxyMode(osNode, install);
666 break;
667 case ARP_BROADCAST_MODE:
668 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900669
670 // we do not add fake gateway ARP rules for FLAT network
671 // ARP packets generated by FLAT typed VM should not be
672 // delegated to switch to handle
673 osNetworkService.subnets().stream().filter(subnet ->
674 osNetworkService.network(subnet.getNetworkId()) != null &&
675 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900676 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900677 .forEach(subnet ->
678 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900679 break;
680 default:
681 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900682 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900683 break;
Jian Lieae12362018-04-10 18:48:32 +0900684 }
685 }
Jian Lif96685c2018-05-21 14:14:16 +0900686
687 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
688 TrafficSelector selector = DefaultTrafficSelector.builder()
689 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
690 .build();
691
692 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
693 .punt()
694 .build();
695
696 osFlowRuleService.setRule(
697 appId,
698 osNode.intgBridge(),
699 selector,
700 treatment,
701 PRIORITY_ARP_CONTROL_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900702 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900703 install
704 );
705 }
706
707 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
708 TrafficSelector selector = DefaultTrafficSelector.builder()
709 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
710 .matchArpOp(ARP.OP_REQUEST)
711 .build();
712
713 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
714 .setOutput(PortNumber.FLOOD)
715 .build();
716
717 osFlowRuleService.setRule(
718 appId,
719 osNode.intgBridge(),
720 selector,
721 treatment,
Jian Li5c09e212018-10-24 18:23:58 +0900722 PRIORITY_ARP_FLOOD_RULE,
723 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900724 install
725 );
726 }
Jian Li7f70bb72018-07-06 23:35:30 +0900727
Jian Li5b66ce02018-07-09 22:43:54 +0900728 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900729 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900730 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900731 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900732 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
733 .forEach(p -> {
734 setArpRequestRule(p, install);
735 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900736 });
737 }
738 }
Jian Lieae12362018-04-10 18:48:32 +0900739 }
740
741 /**
742 * An internal instance port listener which listens the port events generated
743 * from VM. When ARP a host which located in a remote compute node, we specify
744 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
745 * host IP address. When ARP a host which located in a local compute node,
746 * we specify only ARP OP mode as REQUEST.
747 */
748 private class InternalInstancePortListener implements InstancePortListener {
749
750 @Override
751 public boolean isRelevant(InstancePortEvent event) {
752
Jian Li7f70bb72018-07-06 23:35:30 +0900753 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900754 return false;
755 }
756
757 InstancePort instPort = event.subject();
758 return mastershipService.isLocalMaster(instPort.deviceId());
759 }
760
761 @Override
762 public void event(InstancePortEvent event) {
763 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900764 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900765 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Li32b03622018-11-06 17:54:24 +0900766 eventExecutor.execute(() -> {
767 setArpRequestRule(event.subject(), true);
768 setArpReplyRule(event.subject(), true);
769 });
Jian Lieae12362018-04-10 18:48:32 +0900770 break;
771 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li32b03622018-11-06 17:54:24 +0900772 eventExecutor.execute(() -> {
773 setArpRequestRule(event.subject(), false);
774 setArpReplyRule(event.subject(), false);
775 });
Jian Lieae12362018-04-10 18:48:32 +0900776 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900777 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li32b03622018-11-06 17:54:24 +0900778 eventExecutor.execute(() -> {
779 setArpRequestRule(event.subject(), true);
780 setArpReplyRule(event.subject(), true);
781 });
Jian Liec5c32b2018-07-13 14:28:58 +0900782 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900783 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li32b03622018-11-06 17:54:24 +0900784 eventExecutor.execute(() -> {
785 InstancePort revisedInstPort = swapStaleLocation(event.subject());
786 setArpRequestRule(revisedInstPort, false);
787 });
Jian Li24ec59f2018-05-23 19:01:25 +0900788 break;
Jian Lieae12362018-04-10 18:48:32 +0900789 default:
790 break;
791 }
792 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900793 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700794}