blob: 95661a3fe7fb2e3af81197148228b6df7eca7ef1 [file] [log] [blame]
Hyunsun Moonb974fca2016-06-30 21:20:39 -07001/*
Jian Li8e7e6cd2018-03-30 13:33:08 +09002 * Copyright 2016-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Hyunsun Moon05400872017-02-07 17:11:25 +090016package org.onosproject.openstacknetworking.impl;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070017
18import com.google.common.base.Strings;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.ARP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090027import org.onlab.packet.EthType;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070028import org.onlab.packet.Ethernet;
29import org.onlab.packet.Ip4Address;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.util.Tools;
Hyunsun Moon44aac662017-02-18 02:07:01 +090033import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090034import org.onosproject.cfg.ConfigProperty;
Jian Lieae12362018-04-10 18:48:32 +090035import org.onosproject.cluster.ClusterService;
36import org.onosproject.cluster.LeadershipService;
37import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Jian Lieae12362018-04-10 18:48:32 +090040import org.onosproject.mastership.MastershipService;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.device.DeviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090043import org.onosproject.net.flow.DefaultTrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070044import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090045import org.onosproject.net.flow.TrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070046import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.packet.DefaultOutboundPacket;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090051import org.onosproject.openstacknetworking.api.InstancePort;
Jian Lieae12362018-04-10 18:48:32 +090052import org.onosproject.openstacknetworking.api.InstancePortEvent;
53import org.onosproject.openstacknetworking.api.InstancePortListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Lieae12362018-04-10 18:48:32 +090055import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090056import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
57import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
58import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lieae12362018-04-10 18:48:32 +090059import org.onosproject.openstacknode.api.OpenstackNode;
60import org.onosproject.openstacknode.api.OpenstackNodeEvent;
61import org.onosproject.openstacknode.api.OpenstackNodeListener;
62import org.onosproject.openstacknode.api.OpenstackNodeService;
63import org.openstack4j.model.network.Network;
64import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090065import org.openstack4j.model.network.Subnet;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070066import org.osgi.service.component.ComponentContext;
67import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069
Hyunsun Moonb974fca2016-06-30 21:20:39 -070070import java.nio.ByteBuffer;
71import java.util.Dictionary;
Jian Lieae12362018-04-10 18:48:32 +090072import java.util.Objects;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070073import java.util.Set;
74
75import static com.google.common.base.Preconditions.checkNotNull;
Jian Lieae12362018-04-10 18:48:32 +090076import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
77import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
Jian Li5c09e212018-10-24 18:23:58 +090078import static org.onosproject.openstacknetworking.api.Constants.ARP_TABLE;
Jian Lieae12362018-04-10 18:48:32 +090079import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
Hyunsun Moon44aac662017-02-18 02:07:01 +090080import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
81import 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;
Jian Li7f70bb72018-07-06 23:35:30 +090088import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090089import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090090import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
91import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070092
93/**
94 * Handles ARP packet from VMs.
95 */
96@Component(immediate = true)
Hyunsun Moon44aac662017-02-18 02:07:01 +090097public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -070098
99 private final Logger log = LoggerFactory.getLogger(getClass());
100
101 private static final String GATEWAY_MAC = "gatewayMac";
Jian Lieae12362018-04-10 18:48:32 +0900102 private static final String ARP_MODE = "arpMode";
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800105 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800108 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900111 OpenstackFlowRuleService osFlowRuleService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800114 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900117 ClusterService clusterService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 LeadershipService leadershipService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 DeviceService deviceService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 MastershipService mastershipService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800129 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800132 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700133
Jian Lieae12362018-04-10 18:48:32 +0900134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected OpenstackNodeService osNodeService;
136
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700137 @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700138 label = "Fake MAC address for virtual network subnet gateway")
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700139 private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700140
Jian Lieae12362018-04-10 18:48:32 +0900141 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900142 label = "ARP processing mode, broadcast | proxy (default)")
Jian Lieae12362018-04-10 18:48:32 +0900143 protected String arpMode = DEFAULT_ARP_MODE_STR;
144
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700145 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900146 private final InternalOpenstackNetworkListener osNetworkListener =
147 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900148 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
149 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
150
Hyunsun Moon44aac662017-02-18 02:07:01 +0900151
152 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900153 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700154
155 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800156 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900157 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
158 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900159 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900161 osNodeService.addListener(osNodeListener);
162 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700163 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900164
165 instancePortService.addListener(instancePortListener);
166
Hyunsun Moon44aac662017-02-18 02:07:01 +0900167 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700168 }
169
170 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800171 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700172 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900174 osNodeService.removeListener(osNodeListener);
175 instancePortService.removeListener(instancePortListener);
176 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900177 configService.unregisterProperties(getClass(), false);
178
179 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700180 }
181
182 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800183 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900184 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900185
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700186 log.info("Modified");
187 }
188
Jian Li7f70bb72018-07-06 23:35:30 +0900189 private String getArpMode() {
190 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
191 return getPropertyValue(properties, ARP_MODE);
192 }
193
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700194 /**
195 * Processes ARP request packets.
196 * It checks if the target IP is owned by a known host first and then ask to
197 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
198 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900199 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700200 * @param ethPacket ethernet packet
201 */
202 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900203
204 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900205 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900206 return;
207 }
208
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700209 ARP arpPacket = (ARP) ethPacket.getPayload();
210 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
211 return;
212 }
213
Hyunsun Moon44aac662017-02-18 02:07:01 +0900214 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
215 if (srcInstPort == null) {
216 log.trace("Failed to find source instance port(MAC:{})",
217 ethPacket.getSourceMAC());
218 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700219 }
220
Hyunsun Moon44aac662017-02-18 02:07:01 +0900221 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Daniel Park4d7f88b2018-09-19 19:03:38 +0900222
Jian Liac30e272018-10-18 23:08:03 +0900223 MacAddress replyMac = isGatewayIp(targetIp) ? MacAddress.valueOf(gatewayMac) :
Hyunsun Moon44aac662017-02-18 02:07:01 +0900224 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700225 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900226 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700227 return;
228 }
229
230 Ethernet ethReply = ARP.buildArpReply(
231 targetIp.getIp4Address(),
232 replyMac,
233 ethPacket);
234
235 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
236 .setOutput(context.inPacket().receivedFrom().port())
237 .build();
238
239 packetService.emit(new DefaultOutboundPacket(
240 context.inPacket().receivedFrom().deviceId(),
241 treatment,
242 ByteBuffer.wrap(ethReply.serialize())));
243 }
244
Jian Liac30e272018-10-18 23:08:03 +0900245 /**
246 * Denotes whether the given target IP is gateway IP.
247 *
248 * @param targetIp target IP address
249 * @return true if the given targetIP is gateway IP, false otherwise.
250 */
251 private boolean isGatewayIp(IpAddress targetIp) {
Daniel Park4d7f88b2018-09-19 19:03:38 +0900252 return osNetworkService.subnets().stream()
Jian Liac30e272018-10-18 23:08:03 +0900253 .filter(Objects::nonNull)
254 .filter(subnet -> subnet.getGateway() != null)
Daniel Park4d7f88b2018-09-19 19:03:38 +0900255 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
256 }
257
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700258 /**
259 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900260 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700261 *
262 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900263 * @param osNetId openstack network id of the source instance port
264 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700265 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900266 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700267 checkNotNull(targetIp);
268
Hyunsun Moon44aac662017-02-18 02:07:01 +0900269 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
270 if (instPort != null) {
271 log.trace("Found MAC from host service for {}", targetIp);
272 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700273 } else {
274 return MacAddress.NONE;
275 }
276 }
277
Jian Lieae12362018-04-10 18:48:32 +0900278 /**
Daniel Park613ac372018-06-28 14:30:11 +0900279 * Installs flow rules which convert ARP request packet into ARP reply
280 * by adding a fake gateway MAC address as Source Hardware Address.
281 *
282 * @param osSubnet openstack subnet
283 * @param install flag which indicates whether to install rule or remove rule
284 */
285 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
286
Jian Li7f70bb72018-07-06 23:35:30 +0900287 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li8e365bd2018-10-12 22:09:03 +0900288
289 // do not remove fake gateway ARP rules, if there is another gateway
290 // which has the same subnet that to be removed
291 // this only occurs if we have duplicated subnets associated with
292 // different networks
293 if (!install) {
294 long numOfDupGws = osNetworkService.subnets().stream()
295 .filter(s -> !s.getId().equals(osSubnet.getId()))
Jian Li7ce775a2018-10-18 07:24:53 +0900296 .filter(s -> s.getGateway() != null)
Jian Li8e365bd2018-10-12 22:09:03 +0900297 .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
298 .count();
299 if (numOfDupGws > 0) {
300 return;
301 }
302 }
303
Daniel Park613ac372018-06-28 14:30:11 +0900304 String gateway = osSubnet.getGateway();
Daniel Park6a2d95e2018-11-05 18:50:16 +0900305 if (gateway == null) {
306 return;
307 }
Daniel Park613ac372018-06-28 14:30:11 +0900308
309 TrafficSelector selector = DefaultTrafficSelector.builder()
310 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
311 .matchArpOp(ARP.OP_REQUEST)
312 .matchArpTpa(Ip4Address.valueOf(gateway))
313 .build();
314
315 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
316 .setArpOp(ARP.OP_REPLY)
317 .setArpSha(MacAddress.valueOf(gatewayMac))
318 .setArpSpa(Ip4Address.valueOf(gateway))
319 .setOutput(PortNumber.IN_PORT)
320 .build();
321
322 if (osNode == null) {
323 osNodeService.completeNodes(COMPUTE).forEach(n ->
324 osFlowRuleService.setRule(
325 appId,
326 n.intgBridge(),
327 selector,
328 treatment,
329 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900330 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900331 install
332 )
333 );
334 } else {
335 osFlowRuleService.setRule(
336 appId,
337 osNode.intgBridge(),
338 selector,
339 treatment,
340 PRIORITY_ARP_GATEWAY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900341 ARP_TABLE,
Daniel Park613ac372018-06-28 14:30:11 +0900342 install
343 );
344 }
345
346 }
347 }
348
349 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900350 * Installs flow rules to match ARP request packets.
351 *
352 * @param port instance port
353 * @param install installation flag
354 */
355 private void setArpRequestRule(InstancePort port, boolean install) {
356 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
357
358 switch (type) {
359 case VXLAN:
360 setRemoteArpRequestRuleForVxlan(port, install);
361 break;
362 case VLAN:
363 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
364 // there is no need to add any flow rules to handle ARP request
365 break;
366 default:
367 break;
368 }
369 }
370
371 /**
372 * Installs flow rules to match ARP reply packets.
373 *
374 * @param port instance port
375 * @param install installation flag
376 */
377 private void setArpReplyRule(InstancePort port, boolean install) {
378 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
379
380 switch (type) {
381 case VXLAN:
382 setArpReplyRuleForVxlan(port, install);
383 break;
384 case VLAN:
385 setArpReplyRuleForVlan(port, install);
386 break;
387 default:
388 break;
389 }
390 }
391
392 /**
393 * Installs flow rules to match ARP request packets only for VxLAN.
394 *
395 * @param port instance port
396 * @param install installation flag
397 */
398 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
399
400 OpenstackNode localNode = osNodeService.node(port.deviceId());
401
Jian Li5c09e212018-10-24 18:23:58 +0900402 String segId = osNetworkService.segmentId(port.networkId());
403
Jian Li7f70bb72018-07-06 23:35:30 +0900404 TrafficSelector selector = DefaultTrafficSelector.builder()
405 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
406 .matchArpOp(ARP.OP_REQUEST)
407 .matchArpTpa(port.ipAddress().getIp4Address())
Jian Li5c09e212018-10-24 18:23:58 +0900408 .matchTunnelId(Long.valueOf(segId))
Jian Li7f70bb72018-07-06 23:35:30 +0900409 .build();
410
411 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
412 }
413
414 /**
415 * Installs flow rules to match ARP reply packets only for VxLAN.
416 *
417 * @param port instance port
418 * @param install installation flag
419 */
420 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
421
422 OpenstackNode localNode = osNodeService.node(port.deviceId());
423
424 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
425 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
426 }
427
428 /**
429 * Installs flow rules to match ARP reply packets only for VLAN.
430 *
431 * @param port instance port
432 * @param install installation flag
433 */
434 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
435
436 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
437 setRemoteArpTreatmentForVlan(selector, port, install);
438 }
439
440 // a helper method
441 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
442 TrafficSelector selector = DefaultTrafficSelector.builder()
443 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
444 .matchArpOp(ARP.OP_REPLY)
445 .matchArpTpa(port.ipAddress().getIp4Address())
446 .matchArpTha(port.macAddress())
447 .build();
448
449 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
450 .setOutput(port.portNumber())
451 .build();
452
453 osFlowRuleService.setRule(
454 appId,
455 port.deviceId(),
456 selector,
457 treatment,
458 PRIORITY_ARP_REPLY_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900459 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900460 install
461 );
462
463 return selector;
464 }
465
466 // a helper method
467 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
468 InstancePort port,
469 OpenstackNode localNode,
470 boolean install) {
471 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
472 if (!remoteNode.intgBridge().equals(port.deviceId())) {
473 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
474 .extension(buildExtension(
475 deviceService,
476 remoteNode.intgBridge(),
477 localNode.dataIp().getIp4Address()),
478 remoteNode.intgBridge())
479 .setOutput(remoteNode.tunnelPortNum())
480 .build();
481
482 osFlowRuleService.setRule(
483 appId,
484 remoteNode.intgBridge(),
485 selector,
486 treatmentToRemote,
487 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900488 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900489 install
490 );
491 }
492 }
493 }
494
495 // a helper method
496 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
497 InstancePort port,
498 boolean install) {
499 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
500 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
501 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
502 .setOutput(remoteNode.vlanPortNum())
503 .build();
504
505 osFlowRuleService.setRule(
506 appId,
507 remoteNode.intgBridge(),
508 selector,
509 treatmentToRemote,
510 PRIORITY_ARP_REQUEST_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900511 ARP_TABLE,
Jian Li7f70bb72018-07-06 23:35:30 +0900512 install);
513 }
514 }
515 }
516
517 /**
518 * Extracts properties from the component configuration context.
519 *
520 * @param context the component context
521 */
522 private void readComponentConfiguration(ComponentContext context) {
523 Dictionary<?, ?> properties = context.getProperties();
524
525 String updatedMac = Tools.get(properties, GATEWAY_MAC);
526 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
527 log.info("Configured. Gateway MAC is {}", gatewayMac);
528 }
529
530 /**
Jian Lieae12362018-04-10 18:48:32 +0900531 * An internal packet processor which processes ARP request, and results in
532 * packet-out ARP reply.
533 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700534 private class InternalPacketProcessor implements PacketProcessor {
535
536 @Override
537 public void process(PacketContext context) {
538 if (context.isHandled()) {
539 return;
540 }
541
542 Ethernet ethPacket = context.inPacket().parsed();
543 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
544 return;
545 }
546 processPacketIn(context, ethPacket);
547 }
548 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900549
Jian Lieae12362018-04-10 18:48:32 +0900550 /**
551 * An internal network listener which listens to openstack network event,
552 * manages the gateway collection and installs flow rule that handles
553 * ARP request in data plane.
554 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900555 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
556
557 @Override
558 public boolean isRelevant(OpenstackNetworkEvent event) {
559 Subnet osSubnet = event.subnet();
560 if (osSubnet == null) {
561 return false;
562 }
Jian Lieae12362018-04-10 18:48:32 +0900563
Jian Libb4f5412018-04-12 09:48:50 +0900564 Network network = osNetworkService.network(osSubnet.getNetworkId());
565
Jian Lieae12362018-04-10 18:48:32 +0900566 if (network == null) {
567 log.warn("Network is not specified.");
568 return false;
569 } else {
570 if (network.getNetworkType().equals(NetworkType.FLAT)) {
571 return false;
572 }
573 }
574
575 // do not allow to proceed without leadership
576 NodeId leader = leadershipService.getLeader(appId.name());
577 if (!Objects.equals(localNodeId, leader)) {
578 return false;
579 }
580
Hyunsun Moon44aac662017-02-18 02:07:01 +0900581 return !Strings.isNullOrEmpty(osSubnet.getGateway());
582 }
583
584 @Override
585 public void event(OpenstackNetworkEvent event) {
586 switch (event.type()) {
587 case OPENSTACK_SUBNET_CREATED:
588 case OPENSTACK_SUBNET_UPDATED:
Daniel Park613ac372018-06-28 14:30:11 +0900589 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900590 break;
591 case OPENSTACK_SUBNET_REMOVED:
Daniel Park613ac372018-06-28 14:30:11 +0900592 setFakeGatewayArpRule(event.subnet(), false, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900593 break;
594 case OPENSTACK_NETWORK_CREATED:
595 case OPENSTACK_NETWORK_UPDATED:
596 case OPENSTACK_NETWORK_REMOVED:
597 case OPENSTACK_PORT_CREATED:
598 case OPENSTACK_PORT_UPDATED:
599 case OPENSTACK_PORT_REMOVED:
600 default:
601 // do nothing for the other events
602 break;
603 }
604 }
Jian Lieae12362018-04-10 18:48:32 +0900605 }
606
607 /**
608 * An internal openstack node listener which is used for listening openstack
609 * node activity. As long as a node is in complete state, we will install
610 * default ARP rule to handle ARP request.
611 */
612 private class InternalNodeEventListener implements OpenstackNodeListener {
613
614 @Override
615 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900616
Jian Lieae12362018-04-10 18:48:32 +0900617 // do not allow to proceed without leadership
618 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900619 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900620 }
621
622 @Override
623 public void event(OpenstackNodeEvent event) {
624 OpenstackNode osNode = event.subject();
625 switch (event.type()) {
626 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900627 setDefaultArpRule(osNode, true);
Jian Li5b66ce02018-07-09 22:43:54 +0900628 setAllArpRules(osNode, true);
Jian Lieae12362018-04-10 18:48:32 +0900629 break;
630 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900631 setDefaultArpRule(osNode, false);
Jian Li5b66ce02018-07-09 22:43:54 +0900632 setAllArpRules(osNode, false);
Jian Lieae12362018-04-10 18:48:32 +0900633 break;
Jian Lieae12362018-04-10 18:48:32 +0900634 default:
635 break;
636 }
637 }
638
Jian Lif96685c2018-05-21 14:14:16 +0900639 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900640
641 if (getArpMode() == null) {
642 return;
643 }
644
Jian Li7f70bb72018-07-06 23:35:30 +0900645 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900646 case ARP_PROXY_MODE:
647 setDefaultArpRuleForProxyMode(osNode, install);
648 break;
649 case ARP_BROADCAST_MODE:
650 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900651
652 // we do not add fake gateway ARP rules for FLAT network
653 // ARP packets generated by FLAT typed VM should not be
654 // delegated to switch to handle
655 osNetworkService.subnets().stream().filter(subnet ->
656 osNetworkService.network(subnet.getNetworkId()) != null &&
657 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900658 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900659 .forEach(subnet ->
660 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900661 break;
662 default:
663 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900664 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900665 break;
Jian Lieae12362018-04-10 18:48:32 +0900666 }
667 }
Jian Lif96685c2018-05-21 14:14:16 +0900668
669 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
670 TrafficSelector selector = DefaultTrafficSelector.builder()
671 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
672 .build();
673
674 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
675 .punt()
676 .build();
677
678 osFlowRuleService.setRule(
679 appId,
680 osNode.intgBridge(),
681 selector,
682 treatment,
683 PRIORITY_ARP_CONTROL_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900684 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900685 install
686 );
687 }
688
689 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
690 TrafficSelector selector = DefaultTrafficSelector.builder()
691 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
692 .matchArpOp(ARP.OP_REQUEST)
693 .build();
694
695 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
696 .setOutput(PortNumber.FLOOD)
697 .build();
698
699 osFlowRuleService.setRule(
700 appId,
701 osNode.intgBridge(),
702 selector,
703 treatment,
Jian Li5c09e212018-10-24 18:23:58 +0900704 PRIORITY_ARP_FLOOD_RULE,
705 ARP_TABLE,
Jian Lif96685c2018-05-21 14:14:16 +0900706 install
707 );
708 }
Jian Li7f70bb72018-07-06 23:35:30 +0900709
Jian Li5b66ce02018-07-09 22:43:54 +0900710 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900711 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900712 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900713 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900714 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
715 .forEach(p -> {
716 setArpRequestRule(p, install);
717 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900718 });
719 }
720 }
Jian Lieae12362018-04-10 18:48:32 +0900721 }
722
723 /**
724 * An internal instance port listener which listens the port events generated
725 * from VM. When ARP a host which located in a remote compute node, we specify
726 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
727 * host IP address. When ARP a host which located in a local compute node,
728 * we specify only ARP OP mode as REQUEST.
729 */
730 private class InternalInstancePortListener implements InstancePortListener {
731
732 @Override
733 public boolean isRelevant(InstancePortEvent event) {
734
Jian Li7f70bb72018-07-06 23:35:30 +0900735 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900736 return false;
737 }
738
739 InstancePort instPort = event.subject();
740 return mastershipService.isLocalMaster(instPort.deviceId());
741 }
742
743 @Override
744 public void event(InstancePortEvent event) {
745 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900746 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900747 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lieae12362018-04-10 18:48:32 +0900748 setArpRequestRule(event.subject(), true);
749 setArpReplyRule(event.subject(), true);
750 break;
751 case OPENSTACK_INSTANCE_PORT_VANISHED:
752 setArpRequestRule(event.subject(), false);
753 setArpReplyRule(event.subject(), false);
754 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900755 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
756 setArpRequestRule(event.subject(), true);
757 setArpReplyRule(event.subject(), true);
758 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900759 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900760 InstancePort revisedInstPort = swapStaleLocation(event.subject());
761 setArpRequestRule(revisedInstPort, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900762 break;
Jian Lieae12362018-04-10 18:48:32 +0900763 default:
764 break;
765 }
766 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900767 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700768}