blob: c78134dcdaf75efe1814cc94f2c7d9bc6549d796 [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;
Jian Lieae12362018-04-10 18:48:32 +090078import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
Hyunsun Moon44aac662017-02-18 02:07:01 +090079import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
80import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lieae12362018-04-10 18:48:32 +090081import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li5c09e212018-10-24 18:23:58 +090082import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_FLOOD_RULE;
Jian Lieae12362018-04-10 18:48:32 +090083import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
84import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
85import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
Jian Lic2403592018-07-18 12:56:45 +090086import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Li7f70bb72018-07-06 23:35:30 +090087import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090088import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090089import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
90import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070091
92/**
93 * Handles ARP packet from VMs.
94 */
95@Component(immediate = true)
Hyunsun Moon44aac662017-02-18 02:07:01 +090096public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -070097
98 private final Logger log = LoggerFactory.getLogger(getClass());
99
100 private static final String GATEWAY_MAC = "gatewayMac";
Jian Lieae12362018-04-10 18:48:32 +0900101 private static final String ARP_MODE = "arpMode";
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700102
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800104 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800107 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700108
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900110 OpenstackFlowRuleService osFlowRuleService;
111
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800113 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900114
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900116 ClusterService clusterService;
117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900119 LeadershipService leadershipService;
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900122 DeviceService deviceService;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900125 MastershipService mastershipService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800128 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800131 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieae12362018-04-10 18:48:32 +0900134 protected OpenstackNodeService osNodeService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 //@Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
137 // label = "Fake MAC address for virtual network subnet gateway")
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700138 private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 //@Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
141 // label = "ARP processing mode, broadcast | proxy (default)")
Jian Lieae12362018-04-10 18:48:32 +0900142 protected String arpMode = DEFAULT_ARP_MODE_STR;
143
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700144 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900145 private final InternalOpenstackNetworkListener osNetworkListener =
146 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900147 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
148 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
149
Hyunsun Moon44aac662017-02-18 02:07:01 +0900150
151 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900152 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700153
154 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800155 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900156 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
157 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900158 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900159 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900160 osNodeService.addListener(osNodeListener);
161 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700162 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900163
164 instancePortService.addListener(instancePortListener);
165
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700167 }
168
169 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800170 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700171 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900172 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900173 osNodeService.removeListener(osNodeListener);
174 instancePortService.removeListener(instancePortListener);
175 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 configService.unregisterProperties(getClass(), false);
177
178 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700179 }
180
181 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800182 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900183 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900184
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700185 log.info("Modified");
186 }
187
Jian Li7f70bb72018-07-06 23:35:30 +0900188 private String getArpMode() {
189 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
190 return getPropertyValue(properties, ARP_MODE);
191 }
192
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700193 /**
194 * Processes ARP request packets.
195 * It checks if the target IP is owned by a known host first and then ask to
196 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
197 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900198 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700199 * @param ethPacket ethernet packet
200 */
201 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900202
203 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900204 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900205 return;
206 }
207
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700208 ARP arpPacket = (ARP) ethPacket.getPayload();
209 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
210 return;
211 }
212
Hyunsun Moon44aac662017-02-18 02:07:01 +0900213 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
214 if (srcInstPort == null) {
215 log.trace("Failed to find source instance port(MAC:{})",
216 ethPacket.getSourceMAC());
217 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700218 }
219
Hyunsun Moon44aac662017-02-18 02:07:01 +0900220 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Daniel Park4d7f88b2018-09-19 19:03:38 +0900221
Jian Liac30e272018-10-18 23:08:03 +0900222 MacAddress replyMac = isGatewayIp(targetIp) ? MacAddress.valueOf(gatewayMac) :
Hyunsun Moon44aac662017-02-18 02:07:01 +0900223 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700224 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900225 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700226 return;
227 }
228
229 Ethernet ethReply = ARP.buildArpReply(
230 targetIp.getIp4Address(),
231 replyMac,
232 ethPacket);
233
234 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
235 .setOutput(context.inPacket().receivedFrom().port())
236 .build();
237
238 packetService.emit(new DefaultOutboundPacket(
239 context.inPacket().receivedFrom().deviceId(),
240 treatment,
241 ByteBuffer.wrap(ethReply.serialize())));
242 }
243
Jian Liac30e272018-10-18 23:08:03 +0900244 /**
245 * Denotes whether the given target IP is gateway IP.
246 *
247 * @param targetIp target IP address
248 * @return true if the given targetIP is gateway IP, false otherwise.
249 */
250 private boolean isGatewayIp(IpAddress targetIp) {
Daniel Park4d7f88b2018-09-19 19:03:38 +0900251 return osNetworkService.subnets().stream()
Jian Liac30e272018-10-18 23:08:03 +0900252 .filter(Objects::nonNull)
253 .filter(subnet -> subnet.getGateway() != null)
Daniel Park4d7f88b2018-09-19 19:03:38 +0900254 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
255 }
256
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700257 /**
258 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900259 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700260 *
261 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900262 * @param osNetId openstack network id of the source instance port
263 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700264 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900265 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700266 checkNotNull(targetIp);
267
Hyunsun Moon44aac662017-02-18 02:07:01 +0900268 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
269 if (instPort != null) {
270 log.trace("Found MAC from host service for {}", targetIp);
271 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700272 } else {
273 return MacAddress.NONE;
274 }
275 }
276
Jian Lieae12362018-04-10 18:48:32 +0900277 /**
Daniel Park613ac372018-06-28 14:30:11 +0900278 * Installs flow rules which convert ARP request packet into ARP reply
279 * by adding a fake gateway MAC address as Source Hardware Address.
280 *
281 * @param osSubnet openstack subnet
282 * @param install flag which indicates whether to install rule or remove rule
283 */
284 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
285
Jian Li7f70bb72018-07-06 23:35:30 +0900286 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li8e365bd2018-10-12 22:09:03 +0900287
288 // do not remove fake gateway ARP rules, if there is another gateway
289 // which has the same subnet that to be removed
290 // this only occurs if we have duplicated subnets associated with
291 // different networks
292 if (!install) {
293 long numOfDupGws = osNetworkService.subnets().stream()
294 .filter(s -> !s.getId().equals(osSubnet.getId()))
Jian Li7ce775a2018-10-18 07:24:53 +0900295 .filter(s -> s.getGateway() != null)
Jian Li8e365bd2018-10-12 22:09:03 +0900296 .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
297 .count();
298 if (numOfDupGws > 0) {
299 return;
300 }
301 }
302
Daniel Park613ac372018-06-28 14:30:11 +0900303 String gateway = osSubnet.getGateway();
304
305 TrafficSelector selector = DefaultTrafficSelector.builder()
306 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
307 .matchArpOp(ARP.OP_REQUEST)
308 .matchArpTpa(Ip4Address.valueOf(gateway))
309 .build();
310
311 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
312 .setArpOp(ARP.OP_REPLY)
313 .setArpSha(MacAddress.valueOf(gatewayMac))
314 .setArpSpa(Ip4Address.valueOf(gateway))
315 .setOutput(PortNumber.IN_PORT)
316 .build();
317
318 if (osNode == null) {
319 osNodeService.completeNodes(COMPUTE).forEach(n ->
320 osFlowRuleService.setRule(
321 appId,
322 n.intgBridge(),
323 selector,
324 treatment,
325 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900326 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900327 install
328 )
329 );
330 } else {
331 osFlowRuleService.setRule(
332 appId,
333 osNode.intgBridge(),
334 selector,
335 treatment,
336 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900337 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900338 install
339 );
340 }
341
342 }
343 }
344
345 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900346 * Installs flow rules to match ARP request packets.
347 *
348 * @param port instance port
349 * @param install installation flag
350 */
351 private void setArpRequestRule(InstancePort port, boolean install) {
352 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
353
354 switch (type) {
355 case VXLAN:
356 setRemoteArpRequestRuleForVxlan(port, install);
357 break;
358 case VLAN:
359 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
360 // there is no need to add any flow rules to handle ARP request
361 break;
362 default:
363 break;
364 }
365 }
366
367 /**
368 * Installs flow rules to match ARP reply packets.
369 *
370 * @param port instance port
371 * @param install installation flag
372 */
373 private void setArpReplyRule(InstancePort port, boolean install) {
374 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
375
376 switch (type) {
377 case VXLAN:
378 setArpReplyRuleForVxlan(port, install);
379 break;
380 case VLAN:
381 setArpReplyRuleForVlan(port, install);
382 break;
383 default:
384 break;
385 }
386 }
387
388 /**
389 * Installs flow rules to match ARP request packets only for VxLAN.
390 *
391 * @param port instance port
392 * @param install installation flag
393 */
394 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
395
396 OpenstackNode localNode = osNodeService.node(port.deviceId());
397
Jian Li5c09e212018-10-24 18:23:58 +0900398 String segId = osNetworkService.segmentId(port.networkId());
399
Jian Li7f70bb72018-07-06 23:35:30 +0900400 TrafficSelector selector = DefaultTrafficSelector.builder()
401 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
402 .matchArpOp(ARP.OP_REQUEST)
403 .matchArpTpa(port.ipAddress().getIp4Address())
Jian Li5c09e212018-10-24 18:23:58 +0900404 .matchTunnelId(Long.valueOf(segId))
Jian Li7f70bb72018-07-06 23:35:30 +0900405 .build();
406
407 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
408 }
409
410 /**
411 * Installs flow rules to match ARP reply packets only for VxLAN.
412 *
413 * @param port instance port
414 * @param install installation flag
415 */
416 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
417
418 OpenstackNode localNode = osNodeService.node(port.deviceId());
419
420 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
421 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
422 }
423
424 /**
425 * Installs flow rules to match ARP reply packets only for VLAN.
426 *
427 * @param port instance port
428 * @param install installation flag
429 */
430 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
431
432 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
433 setRemoteArpTreatmentForVlan(selector, port, install);
434 }
435
436 // a helper method
437 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
438 TrafficSelector selector = DefaultTrafficSelector.builder()
439 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
440 .matchArpOp(ARP.OP_REPLY)
441 .matchArpTpa(port.ipAddress().getIp4Address())
442 .matchArpTha(port.macAddress())
443 .build();
444
445 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
446 .setOutput(port.portNumber())
447 .build();
448
449 osFlowRuleService.setRule(
450 appId,
451 port.deviceId(),
452 selector,
453 treatment,
454 PRIORITY_ARP_REPLY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900455 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900456 install
457 );
458
459 return selector;
460 }
461
462 // a helper method
463 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
464 InstancePort port,
465 OpenstackNode localNode,
466 boolean install) {
467 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
468 if (!remoteNode.intgBridge().equals(port.deviceId())) {
469 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
470 .extension(buildExtension(
471 deviceService,
472 remoteNode.intgBridge(),
473 localNode.dataIp().getIp4Address()),
474 remoteNode.intgBridge())
475 .setOutput(remoteNode.tunnelPortNum())
476 .build();
477
478 osFlowRuleService.setRule(
479 appId,
480 remoteNode.intgBridge(),
481 selector,
482 treatmentToRemote,
483 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900484 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900485 install
486 );
487 }
488 }
489 }
490
491 // a helper method
492 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
493 InstancePort port,
494 boolean install) {
495 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
496 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
497 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
498 .setOutput(remoteNode.vlanPortNum())
499 .build();
500
501 osFlowRuleService.setRule(
502 appId,
503 remoteNode.intgBridge(),
504 selector,
505 treatmentToRemote,
506 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900507 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900508 install);
509 }
510 }
511 }
512
513 /**
514 * Extracts properties from the component configuration context.
515 *
516 * @param context the component context
517 */
518 private void readComponentConfiguration(ComponentContext context) {
519 Dictionary<?, ?> properties = context.getProperties();
520
521 String updatedMac = Tools.get(properties, GATEWAY_MAC);
522 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
523 log.info("Configured. Gateway MAC is {}", gatewayMac);
524 }
525
526 /**
Jian Lieae12362018-04-10 18:48:32 +0900527 * An internal packet processor which processes ARP request, and results in
528 * packet-out ARP reply.
529 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700530 private class InternalPacketProcessor implements PacketProcessor {
531
532 @Override
533 public void process(PacketContext context) {
534 if (context.isHandled()) {
535 return;
536 }
537
538 Ethernet ethPacket = context.inPacket().parsed();
539 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
540 return;
541 }
542 processPacketIn(context, ethPacket);
543 }
544 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900545
Jian Lieae12362018-04-10 18:48:32 +0900546 /**
547 * An internal network listener which listens to openstack network event,
548 * manages the gateway collection and installs flow rule that handles
549 * ARP request in data plane.
550 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900551 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
552
553 @Override
554 public boolean isRelevant(OpenstackNetworkEvent event) {
555 Subnet osSubnet = event.subnet();
556 if (osSubnet == null) {
557 return false;
558 }
Jian Lieae12362018-04-10 18:48:32 +0900559
Jian Libb4f5412018-04-12 09:48:50 +0900560 Network network = osNetworkService.network(osSubnet.getNetworkId());
561
Jian Lieae12362018-04-10 18:48:32 +0900562 if (network == null) {
563 log.warn("Network is not specified.");
564 return false;
565 } else {
566 if (network.getNetworkType().equals(NetworkType.FLAT)) {
567 return false;
568 }
569 }
570
571 // do not allow to proceed without leadership
572 NodeId leader = leadershipService.getLeader(appId.name());
573 if (!Objects.equals(localNodeId, leader)) {
574 return false;
575 }
576
Hyunsun Moon44aac662017-02-18 02:07:01 +0900577 return !Strings.isNullOrEmpty(osSubnet.getGateway());
578 }
579
580 @Override
581 public void event(OpenstackNetworkEvent event) {
582 switch (event.type()) {
583 case OPENSTACK_SUBNET_CREATED:
584 case OPENSTACK_SUBNET_UPDATED:
Daniel Park613ac372018-06-28 14:30:11 +0900585 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900586 break;
587 case OPENSTACK_SUBNET_REMOVED:
Daniel Park613ac372018-06-28 14:30:11 +0900588 setFakeGatewayArpRule(event.subnet(), false, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900589 break;
590 case OPENSTACK_NETWORK_CREATED:
591 case OPENSTACK_NETWORK_UPDATED:
592 case OPENSTACK_NETWORK_REMOVED:
593 case OPENSTACK_PORT_CREATED:
594 case OPENSTACK_PORT_UPDATED:
595 case OPENSTACK_PORT_REMOVED:
596 default:
597 // do nothing for the other events
598 break;
599 }
600 }
Jian Lieae12362018-04-10 18:48:32 +0900601 }
602
603 /**
604 * An internal openstack node listener which is used for listening openstack
605 * node activity. As long as a node is in complete state, we will install
606 * default ARP rule to handle ARP request.
607 */
608 private class InternalNodeEventListener implements OpenstackNodeListener {
609
610 @Override
611 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900612
Jian Lieae12362018-04-10 18:48:32 +0900613 // do not allow to proceed without leadership
614 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900615 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900616 }
617
618 @Override
619 public void event(OpenstackNodeEvent event) {
620 OpenstackNode osNode = event.subject();
621 switch (event.type()) {
622 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900623 setDefaultArpRule(osNode, true);
Jian Li5b66ce02018-07-09 22:43:54 +0900624 setAllArpRules(osNode, true);
Jian Lieae12362018-04-10 18:48:32 +0900625 break;
626 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900627 setDefaultArpRule(osNode, false);
Jian Li5b66ce02018-07-09 22:43:54 +0900628 setAllArpRules(osNode, false);
Jian Lieae12362018-04-10 18:48:32 +0900629 break;
Jian Lieae12362018-04-10 18:48:32 +0900630 default:
631 break;
632 }
633 }
634
Jian Lif96685c2018-05-21 14:14:16 +0900635 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900636
637 if (getArpMode() == null) {
638 return;
639 }
640
Jian Li7f70bb72018-07-06 23:35:30 +0900641 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900642 case ARP_PROXY_MODE:
643 setDefaultArpRuleForProxyMode(osNode, install);
644 break;
645 case ARP_BROADCAST_MODE:
646 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900647
648 // we do not add fake gateway ARP rules for FLAT network
649 // ARP packets generated by FLAT typed VM should not be
650 // delegated to switch to handle
651 osNetworkService.subnets().stream().filter(subnet ->
652 osNetworkService.network(subnet.getNetworkId()) != null &&
653 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900654 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900655 .forEach(subnet ->
656 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900657 break;
658 default:
659 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900660 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900661 break;
Jian Lieae12362018-04-10 18:48:32 +0900662 }
663 }
Jian Lif96685c2018-05-21 14:14:16 +0900664
665 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
666 TrafficSelector selector = DefaultTrafficSelector.builder()
667 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
668 .build();
669
670 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
671 .punt()
672 .build();
673
674 osFlowRuleService.setRule(
675 appId,
676 osNode.intgBridge(),
677 selector,
678 treatment,
679 PRIORITY_ARP_CONTROL_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900680 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900681 install
682 );
683 }
684
685 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
686 TrafficSelector selector = DefaultTrafficSelector.builder()
687 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
688 .matchArpOp(ARP.OP_REQUEST)
689 .build();
690
691 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
692 .setOutput(PortNumber.FLOOD)
693 .build();
694
695 osFlowRuleService.setRule(
696 appId,
697 osNode.intgBridge(),
698 selector,
699 treatment,
Jian Li5c09e212018-10-24 18:23:58 +0900700 PRIORITY_ARP_FLOOD_RULE,
701 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900702 install
703 );
704 }
Jian Li7f70bb72018-07-06 23:35:30 +0900705
Jian Li5b66ce02018-07-09 22:43:54 +0900706 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900707 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900708 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900709 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900710 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
711 .forEach(p -> {
712 setArpRequestRule(p, install);
713 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900714 });
715 }
716 }
Jian Lieae12362018-04-10 18:48:32 +0900717 }
718
719 /**
720 * An internal instance port listener which listens the port events generated
721 * from VM. When ARP a host which located in a remote compute node, we specify
722 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
723 * host IP address. When ARP a host which located in a local compute node,
724 * we specify only ARP OP mode as REQUEST.
725 */
726 private class InternalInstancePortListener implements InstancePortListener {
727
728 @Override
729 public boolean isRelevant(InstancePortEvent event) {
730
Jian Li7f70bb72018-07-06 23:35:30 +0900731 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900732 return false;
733 }
734
735 InstancePort instPort = event.subject();
736 return mastershipService.isLocalMaster(instPort.deviceId());
737 }
738
739 @Override
740 public void event(InstancePortEvent event) {
741 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900742 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900743 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lieae12362018-04-10 18:48:32 +0900744 setArpRequestRule(event.subject(), true);
745 setArpReplyRule(event.subject(), true);
746 break;
747 case OPENSTACK_INSTANCE_PORT_VANISHED:
748 setArpRequestRule(event.subject(), false);
749 setArpReplyRule(event.subject(), false);
750 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900751 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
752 setArpRequestRule(event.subject(), true);
753 setArpReplyRule(event.subject(), true);
754 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900755 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900756 InstancePort revisedInstPort = swapStaleLocation(event.subject());
757 setArpRequestRule(revisedInstPort, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900758 break;
Jian Lieae12362018-04-10 18:48:32 +0900759 default:
760 break;
761 }
762 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900763 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700764}