blob: 06265bd15126ea474ea3fb02adbebf26a5ef66da [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;
Daniel Park8a9220f2018-11-19 18:58:35 +090025import org.onlab.packet.VlanId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070026import org.onlab.util.Tools;
Hyunsun Moon44aac662017-02-18 02:07:01 +090027import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090028import org.onosproject.cfg.ConfigProperty;
Jian Lieae12362018-04-10 18:48:32 +090029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Jian Lieae12362018-04-10 18:48:32 +090034import org.onosproject.mastership.MastershipService;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.device.DeviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090037import org.onosproject.net.flow.DefaultTrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070038import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090039import org.onosproject.net.flow.TrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070040import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.packet.DefaultOutboundPacket;
42import org.onosproject.net.packet.PacketContext;
43import org.onosproject.net.packet.PacketProcessor;
44import org.onosproject.net.packet.PacketService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090045import org.onosproject.openstacknetworking.api.InstancePort;
Jian Lieae12362018-04-10 18:48:32 +090046import org.onosproject.openstacknetworking.api.InstancePortEvent;
47import org.onosproject.openstacknetworking.api.InstancePortListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090048import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Lieae12362018-04-10 18:48:32 +090049import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090050import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
51import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
52import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lieae12362018-04-10 18:48:32 +090053import org.onosproject.openstacknode.api.OpenstackNode;
54import org.onosproject.openstacknode.api.OpenstackNodeEvent;
55import org.onosproject.openstacknode.api.OpenstackNodeListener;
56import org.onosproject.openstacknode.api.OpenstackNodeService;
57import org.openstack4j.model.network.Network;
58import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090059import org.openstack4j.model.network.Subnet;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070060import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061import org.osgi.service.component.annotations.Activate;
62import org.osgi.service.component.annotations.Component;
63import org.osgi.service.component.annotations.Deactivate;
64import org.osgi.service.component.annotations.Modified;
65import org.osgi.service.component.annotations.Reference;
66import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070067import 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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090082import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lieae12362018-04-10 18:48:32 +090083import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li5c09e212018-10-24 18:23:58 +090084import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_FLOOD_RULE;
Jian Lieae12362018-04-10 18:48:32 +090085import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
86import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
87import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
Jian Lic2403592018-07-18 12:56:45 +090088import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Ray Milkey8e406512018-10-24 15:56:50 -070089import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE;
90import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
91import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
92import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
Jian Li7f70bb72018-07-06 23:35:30 +090093import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090094import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090095import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
96import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070097
98/**
99 * Handles ARP packet from VMs.
100 */
Ray Milkey8e406512018-10-24 15:56:50 -0700101@Component(
102 immediate = true,
103 property = {
104 GATEWAY_MAC + "=" + GATEWAY_MAC_DEFAULT,
105 ARP_MODE + "=" + ARP_MODE_DEFAULT,
106 }
107)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700109
110 private final Logger log = LoggerFactory.getLogger(getClass());
111
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800113 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900114
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800116 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900119 OpenstackFlowRuleService osFlowRuleService;
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800122 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900125 ClusterService clusterService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900128 LeadershipService leadershipService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900131 DeviceService deviceService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900134 MastershipService mastershipService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800137 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800140 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900143 protected OpenstackNodeService osNodeService;
144
Ray Milkey8e406512018-10-24 15:56:50 -0700145 /** Fake MAC address for virtual network subnet gateway. */
146 private String gatewayMac = GATEWAY_MAC_DEFAULT;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700147
Ray Milkey8e406512018-10-24 15:56:50 -0700148 /** ARP processing mode, broadcast | proxy (default). */
149 protected String arpMode = ARP_MODE_DEFAULT;
Jian Lieae12362018-04-10 18:48:32 +0900150
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700151 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900152 private final InternalOpenstackNetworkListener osNetworkListener =
153 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900154 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
155 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
156
Jian Li32b03622018-11-06 17:54:24 +0900157 private final ExecutorService eventExecutor = newSingleThreadExecutor(
158 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
159
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160
161 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900162 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700163
164 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800165 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
167 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900168 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900170 osNodeService.addListener(osNodeListener);
171 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700172 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900173
174 instancePortService.addListener(instancePortListener);
175
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700177 }
178
179 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800180 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700181 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900182 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900183 osNodeService.removeListener(osNodeListener);
184 instancePortService.removeListener(instancePortListener);
185 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900186 configService.unregisterProperties(getClass(), false);
Jian Li32b03622018-11-06 17:54:24 +0900187 eventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188
189 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700190 }
191
192 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800193 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900194 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900195
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700196 log.info("Modified");
197 }
198
Jian Li7f70bb72018-07-06 23:35:30 +0900199 private String getArpMode() {
200 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
201 return getPropertyValue(properties, ARP_MODE);
202 }
203
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700204 /**
205 * Processes ARP request packets.
206 * It checks if the target IP is owned by a known host first and then ask to
207 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
208 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700210 * @param ethPacket ethernet packet
211 */
212 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900213
214 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900215 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900216 return;
217 }
218
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700219 ARP arpPacket = (ARP) ethPacket.getPayload();
220 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
221 return;
222 }
223
Hyunsun Moon44aac662017-02-18 02:07:01 +0900224 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
225 if (srcInstPort == null) {
226 log.trace("Failed to find source instance port(MAC:{})",
227 ethPacket.getSourceMAC());
228 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700229 }
230
Hyunsun Moon44aac662017-02-18 02:07:01 +0900231 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Daniel Park4d7f88b2018-09-19 19:03:38 +0900232
Jian Liac30e272018-10-18 23:08:03 +0900233 MacAddress replyMac = isGatewayIp(targetIp) ? MacAddress.valueOf(gatewayMac) :
Hyunsun Moon44aac662017-02-18 02:07:01 +0900234 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700235 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900236 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700237 return;
238 }
239
240 Ethernet ethReply = ARP.buildArpReply(
241 targetIp.getIp4Address(),
242 replyMac,
243 ethPacket);
244
245 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
246 .setOutput(context.inPacket().receivedFrom().port())
247 .build();
248
249 packetService.emit(new DefaultOutboundPacket(
250 context.inPacket().receivedFrom().deviceId(),
251 treatment,
252 ByteBuffer.wrap(ethReply.serialize())));
253 }
254
Jian Liac30e272018-10-18 23:08:03 +0900255 /**
256 * Denotes whether the given target IP is gateway IP.
257 *
258 * @param targetIp target IP address
259 * @return true if the given targetIP is gateway IP, false otherwise.
260 */
261 private boolean isGatewayIp(IpAddress targetIp) {
Daniel Park4d7f88b2018-09-19 19:03:38 +0900262 return osNetworkService.subnets().stream()
Jian Liac30e272018-10-18 23:08:03 +0900263 .filter(Objects::nonNull)
264 .filter(subnet -> subnet.getGateway() != null)
Daniel Park4d7f88b2018-09-19 19:03:38 +0900265 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
266 }
267
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700268 /**
269 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900270 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700271 *
272 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900273 * @param osNetId openstack network id of the source instance port
274 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700275 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900276 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700277 checkNotNull(targetIp);
278
Hyunsun Moon44aac662017-02-18 02:07:01 +0900279 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
280 if (instPort != null) {
281 log.trace("Found MAC from host service for {}", targetIp);
282 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700283 } else {
284 return MacAddress.NONE;
285 }
286 }
287
Jian Lieae12362018-04-10 18:48:32 +0900288 /**
Daniel Park613ac372018-06-28 14:30:11 +0900289 * Installs flow rules which convert ARP request packet into ARP reply
290 * by adding a fake gateway MAC address as Source Hardware Address.
291 *
292 * @param osSubnet openstack subnet
293 * @param install flag which indicates whether to install rule or remove rule
294 */
295 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
296
Jian Li7f70bb72018-07-06 23:35:30 +0900297 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li8e365bd2018-10-12 22:09:03 +0900298
299 // do not remove fake gateway ARP rules, if there is another gateway
300 // which has the same subnet that to be removed
301 // this only occurs if we have duplicated subnets associated with
302 // different networks
303 if (!install) {
304 long numOfDupGws = osNetworkService.subnets().stream()
305 .filter(s -> !s.getId().equals(osSubnet.getId()))
Jian Li7ce775a2018-10-18 07:24:53 +0900306 .filter(s -> s.getGateway() != null)
Jian Li8e365bd2018-10-12 22:09:03 +0900307 .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
308 .count();
309 if (numOfDupGws > 0) {
310 return;
311 }
312 }
313
Daniel Park613ac372018-06-28 14:30:11 +0900314 String gateway = osSubnet.getGateway();
Daniel Park6a2d95e2018-11-05 18:50:16 +0900315 if (gateway == null) {
316 return;
317 }
Daniel Park613ac372018-06-28 14:30:11 +0900318
Daniel Park8a9220f2018-11-19 18:58:35 +0900319 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
320 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Daniel Park613ac372018-06-28 14:30:11 +0900321
Daniel Park8a9220f2018-11-19 18:58:35 +0900322 NetworkType type = NetworkType.valueOf(osNetworkService.networkType(osSubnet.getNetworkId()));
323
324 if (type.equals(NetworkType.VLAN)) {
325 sBuilder.matchVlanId(VlanId.vlanId(osNetworkService.segmentId(osSubnet.getNetworkId())));
326 tBuilder.popVlan();
327 }
328
329 sBuilder.matchEthType(EthType.EtherType.ARP.ethType().toShort())
330 .matchArpOp(ARP.OP_REQUEST)
331 .matchArpTpa(Ip4Address.valueOf(gateway));
332
333 tBuilder.setArpOp(ARP.OP_REPLY)
Daniel Park613ac372018-06-28 14:30:11 +0900334 .setArpSha(MacAddress.valueOf(gatewayMac))
335 .setArpSpa(Ip4Address.valueOf(gateway))
Daniel Park8a9220f2018-11-19 18:58:35 +0900336 .setOutput(PortNumber.IN_PORT);
Daniel Park613ac372018-06-28 14:30:11 +0900337
338 if (osNode == null) {
339 osNodeService.completeNodes(COMPUTE).forEach(n ->
340 osFlowRuleService.setRule(
341 appId,
342 n.intgBridge(),
Daniel Park8a9220f2018-11-19 18:58:35 +0900343 sBuilder.build(),
344 tBuilder.build(),
Daniel Park613ac372018-06-28 14:30:11 +0900345 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900346 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900347 install
348 )
349 );
350 } else {
351 osFlowRuleService.setRule(
352 appId,
353 osNode.intgBridge(),
Daniel Park8a9220f2018-11-19 18:58:35 +0900354 sBuilder.build(),
355 tBuilder.build(),
Daniel Park613ac372018-06-28 14:30:11 +0900356 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900357 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900358 install
359 );
360 }
361
362 }
363 }
364
365 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900366 * Installs flow rules to match ARP request packets.
367 *
368 * @param port instance port
369 * @param install installation flag
370 */
371 private void setArpRequestRule(InstancePort port, boolean install) {
372 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
373
374 switch (type) {
375 case VXLAN:
376 setRemoteArpRequestRuleForVxlan(port, install);
377 break;
378 case VLAN:
379 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
380 // there is no need to add any flow rules to handle ARP request
381 break;
382 default:
383 break;
384 }
385 }
386
387 /**
388 * Installs flow rules to match ARP reply packets.
389 *
390 * @param port instance port
391 * @param install installation flag
392 */
393 private void setArpReplyRule(InstancePort port, boolean install) {
394 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
395
396 switch (type) {
397 case VXLAN:
398 setArpReplyRuleForVxlan(port, install);
399 break;
400 case VLAN:
401 setArpReplyRuleForVlan(port, install);
402 break;
403 default:
404 break;
405 }
406 }
407
408 /**
409 * Installs flow rules to match ARP request packets only for VxLAN.
410 *
411 * @param port instance port
412 * @param install installation flag
413 */
414 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
415
416 OpenstackNode localNode = osNodeService.node(port.deviceId());
417
Jian Li5c09e212018-10-24 18:23:58 +0900418 String segId = osNetworkService.segmentId(port.networkId());
419
Jian Li7f70bb72018-07-06 23:35:30 +0900420 TrafficSelector selector = DefaultTrafficSelector.builder()
421 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
422 .matchArpOp(ARP.OP_REQUEST)
423 .matchArpTpa(port.ipAddress().getIp4Address())
Jian Li5c09e212018-10-24 18:23:58 +0900424 .matchTunnelId(Long.valueOf(segId))
Jian Li7f70bb72018-07-06 23:35:30 +0900425 .build();
426
427 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
428 }
429
430 /**
431 * Installs flow rules to match ARP reply packets only for VxLAN.
432 *
433 * @param port instance port
434 * @param install installation flag
435 */
436 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
437
438 OpenstackNode localNode = osNodeService.node(port.deviceId());
439
440 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
441 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
442 }
443
444 /**
445 * Installs flow rules to match ARP reply packets only for VLAN.
446 *
447 * @param port instance port
448 * @param install installation flag
449 */
450 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
451
452 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
453 setRemoteArpTreatmentForVlan(selector, port, install);
454 }
455
456 // a helper method
457 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
458 TrafficSelector selector = DefaultTrafficSelector.builder()
459 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
460 .matchArpOp(ARP.OP_REPLY)
461 .matchArpTpa(port.ipAddress().getIp4Address())
462 .matchArpTha(port.macAddress())
463 .build();
464
465 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
466 .setOutput(port.portNumber())
467 .build();
468
469 osFlowRuleService.setRule(
470 appId,
471 port.deviceId(),
472 selector,
473 treatment,
474 PRIORITY_ARP_REPLY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900475 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900476 install
477 );
478
479 return selector;
480 }
481
482 // a helper method
483 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
484 InstancePort port,
485 OpenstackNode localNode,
486 boolean install) {
487 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
488 if (!remoteNode.intgBridge().equals(port.deviceId())) {
489 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
490 .extension(buildExtension(
491 deviceService,
492 remoteNode.intgBridge(),
493 localNode.dataIp().getIp4Address()),
494 remoteNode.intgBridge())
495 .setOutput(remoteNode.tunnelPortNum())
496 .build();
497
498 osFlowRuleService.setRule(
499 appId,
500 remoteNode.intgBridge(),
501 selector,
502 treatmentToRemote,
503 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900504 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900505 install
506 );
507 }
508 }
509 }
510
511 // a helper method
512 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
513 InstancePort port,
514 boolean install) {
515 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
516 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
517 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
518 .setOutput(remoteNode.vlanPortNum())
519 .build();
520
521 osFlowRuleService.setRule(
522 appId,
523 remoteNode.intgBridge(),
524 selector,
525 treatmentToRemote,
526 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900527 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900528 install);
529 }
530 }
531 }
532
533 /**
534 * Extracts properties from the component configuration context.
535 *
536 * @param context the component context
537 */
538 private void readComponentConfiguration(ComponentContext context) {
539 Dictionary<?, ?> properties = context.getProperties();
540
541 String updatedMac = Tools.get(properties, GATEWAY_MAC);
Ray Milkey8e406512018-10-24 15:56:50 -0700542 gatewayMac = updatedMac != null ? updatedMac : GATEWAY_MAC_DEFAULT;
Jian Li7f70bb72018-07-06 23:35:30 +0900543 log.info("Configured. Gateway MAC is {}", gatewayMac);
544 }
545
546 /**
Jian Lieae12362018-04-10 18:48:32 +0900547 * An internal packet processor which processes ARP request, and results in
548 * packet-out ARP reply.
549 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700550 private class InternalPacketProcessor implements PacketProcessor {
551
552 @Override
553 public void process(PacketContext context) {
554 if (context.isHandled()) {
555 return;
556 }
557
558 Ethernet ethPacket = context.inPacket().parsed();
559 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
560 return;
561 }
Jian Li32b03622018-11-06 17:54:24 +0900562
563 eventExecutor.execute(() -> processPacketIn(context, ethPacket));
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700564 }
565 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900566
Jian Lieae12362018-04-10 18:48:32 +0900567 /**
568 * An internal network listener which listens to openstack network event,
569 * manages the gateway collection and installs flow rule that handles
570 * ARP request in data plane.
571 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900572 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
573
574 @Override
575 public boolean isRelevant(OpenstackNetworkEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900576 return event.subnet() != null;
577 }
Jian Lieae12362018-04-10 18:48:32 +0900578
Jian Li34220ea2018-11-14 01:30:24 +0900579 private boolean isRelevantHelper(Subnet osSubnet) {
Jian Libb4f5412018-04-12 09:48:50 +0900580 Network network = osNetworkService.network(osSubnet.getNetworkId());
581
Jian Lieae12362018-04-10 18:48:32 +0900582 if (network == null) {
583 log.warn("Network is not specified.");
584 return false;
585 } else {
586 if (network.getNetworkType().equals(NetworkType.FLAT)) {
587 return false;
588 }
589 }
590
591 // do not allow to proceed without leadership
592 NodeId leader = leadershipService.getLeader(appId.name());
593 if (!Objects.equals(localNodeId, leader)) {
594 return false;
595 }
596
Hyunsun Moon44aac662017-02-18 02:07:01 +0900597 return !Strings.isNullOrEmpty(osSubnet.getGateway());
598 }
599
600 @Override
601 public void event(OpenstackNetworkEvent event) {
602 switch (event.type()) {
603 case OPENSTACK_SUBNET_CREATED:
604 case OPENSTACK_SUBNET_UPDATED:
Jian Li32b03622018-11-06 17:54:24 +0900605 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900606
607 if (!isRelevantHelper(event.subnet())) {
608 return;
609 }
610
Jian Li32b03622018-11-06 17:54:24 +0900611 setFakeGatewayArpRule(event.subnet(), true, null);
612 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900613 break;
614 case OPENSTACK_SUBNET_REMOVED:
Jian Li32b03622018-11-06 17:54:24 +0900615 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900616
617 if (!isRelevantHelper(event.subnet())) {
618 return;
619 }
620
Jian Li32b03622018-11-06 17:54:24 +0900621 setFakeGatewayArpRule(event.subnet(), false, null);
622 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900623 break;
624 case OPENSTACK_NETWORK_CREATED:
625 case OPENSTACK_NETWORK_UPDATED:
626 case OPENSTACK_NETWORK_REMOVED:
627 case OPENSTACK_PORT_CREATED:
628 case OPENSTACK_PORT_UPDATED:
629 case OPENSTACK_PORT_REMOVED:
630 default:
631 // do nothing for the other events
632 break;
633 }
634 }
Jian Lieae12362018-04-10 18:48:32 +0900635 }
636
637 /**
638 * An internal openstack node listener which is used for listening openstack
639 * node activity. As long as a node is in complete state, we will install
640 * default ARP rule to handle ARP request.
641 */
642 private class InternalNodeEventListener implements OpenstackNodeListener {
Jian Li34220ea2018-11-14 01:30:24 +0900643 private boolean isRelevantHelper() {
644 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Jian Lieae12362018-04-10 18:48:32 +0900645 }
646
647 @Override
648 public void event(OpenstackNodeEvent event) {
649 OpenstackNode osNode = event.subject();
650 switch (event.type()) {
651 case OPENSTACK_NODE_COMPLETE:
Jian Li32b03622018-11-06 17:54:24 +0900652 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900653
654 if (!isRelevantHelper()) {
655 return;
656 }
657
Jian Li32b03622018-11-06 17:54:24 +0900658 setDefaultArpRule(osNode, true);
659 setAllArpRules(osNode, true);
660 });
Jian Lieae12362018-04-10 18:48:32 +0900661 break;
662 case OPENSTACK_NODE_INCOMPLETE:
Jian Li32b03622018-11-06 17:54:24 +0900663 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900664
665 if (!isRelevantHelper()) {
666 return;
667 }
668
Jian Li32b03622018-11-06 17:54:24 +0900669 setDefaultArpRule(osNode, false);
670 setAllArpRules(osNode, false);
671 });
Jian Lieae12362018-04-10 18:48:32 +0900672 break;
Jian Lieae12362018-04-10 18:48:32 +0900673 default:
674 break;
675 }
676 }
677
Jian Lif96685c2018-05-21 14:14:16 +0900678 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900679
680 if (getArpMode() == null) {
681 return;
682 }
683
Jian Li7f70bb72018-07-06 23:35:30 +0900684 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900685 case ARP_PROXY_MODE:
686 setDefaultArpRuleForProxyMode(osNode, install);
687 break;
688 case ARP_BROADCAST_MODE:
689 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900690
691 // we do not add fake gateway ARP rules for FLAT network
692 // ARP packets generated by FLAT typed VM should not be
693 // delegated to switch to handle
694 osNetworkService.subnets().stream().filter(subnet ->
695 osNetworkService.network(subnet.getNetworkId()) != null &&
696 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900697 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900698 .forEach(subnet ->
699 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900700 break;
701 default:
702 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900703 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900704 break;
Jian Lieae12362018-04-10 18:48:32 +0900705 }
706 }
Jian Lif96685c2018-05-21 14:14:16 +0900707
708 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
709 TrafficSelector selector = DefaultTrafficSelector.builder()
710 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
711 .build();
712
713 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
714 .punt()
715 .build();
716
717 osFlowRuleService.setRule(
718 appId,
719 osNode.intgBridge(),
720 selector,
721 treatment,
722 PRIORITY_ARP_CONTROL_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900723 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900724 install
725 );
726 }
727
728 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
729 TrafficSelector selector = DefaultTrafficSelector.builder()
730 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
731 .matchArpOp(ARP.OP_REQUEST)
732 .build();
733
734 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
735 .setOutput(PortNumber.FLOOD)
736 .build();
737
738 osFlowRuleService.setRule(
739 appId,
740 osNode.intgBridge(),
741 selector,
742 treatment,
Jian Li5c09e212018-10-24 18:23:58 +0900743 PRIORITY_ARP_FLOOD_RULE,
744 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900745 install
746 );
747 }
Jian Li7f70bb72018-07-06 23:35:30 +0900748
Jian Li5b66ce02018-07-09 22:43:54 +0900749 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900750 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900751 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900752 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900753 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
754 .forEach(p -> {
755 setArpRequestRule(p, install);
756 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900757 });
758 }
759 }
Jian Lieae12362018-04-10 18:48:32 +0900760 }
761
762 /**
763 * An internal instance port listener which listens the port events generated
764 * from VM. When ARP a host which located in a remote compute node, we specify
765 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
766 * host IP address. When ARP a host which located in a local compute node,
767 * we specify only ARP OP mode as REQUEST.
768 */
769 private class InternalInstancePortListener implements InstancePortListener {
770
771 @Override
772 public boolean isRelevant(InstancePortEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900773 return ARP_BROADCAST_MODE.equals(getArpMode());
774 }
Jian Lieae12362018-04-10 18:48:32 +0900775
Jian Li34220ea2018-11-14 01:30:24 +0900776 private boolean isRelevantHelper(InstancePortEvent event) {
777 return mastershipService.isLocalMaster(event.subject().deviceId());
Jian Lieae12362018-04-10 18:48:32 +0900778 }
779
780 @Override
781 public void event(InstancePortEvent event) {
782 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900783 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900784 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Li34220ea2018-11-14 01:30:24 +0900785 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li32b03622018-11-06 17:54:24 +0900786 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900787
788 if (!isRelevantHelper(event)) {
789 return;
790 }
791
Jian Li32b03622018-11-06 17:54:24 +0900792 setArpRequestRule(event.subject(), true);
793 setArpReplyRule(event.subject(), true);
794 });
Jian Lieae12362018-04-10 18:48:32 +0900795 break;
796 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li32b03622018-11-06 17:54:24 +0900797 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900798
799 if (!isRelevantHelper(event)) {
800 return;
801 }
802
Jian Li32b03622018-11-06 17:54:24 +0900803 setArpRequestRule(event.subject(), false);
804 setArpReplyRule(event.subject(), false);
805 });
Jian Lieae12362018-04-10 18:48:32 +0900806 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900807 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li32b03622018-11-06 17:54:24 +0900808 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900809
810 if (!isRelevantHelper(event)) {
811 return;
812 }
813
Jian Li32b03622018-11-06 17:54:24 +0900814 InstancePort revisedInstPort = swapStaleLocation(event.subject());
815 setArpRequestRule(revisedInstPort, false);
816 });
Jian Li24ec59f2018-05-23 19:01:25 +0900817 break;
Jian Lieae12362018-04-10 18:48:32 +0900818 default:
819 break;
820 }
821 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900822 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700823}