blob: daad9d189bb3d04ecaf75200489f4957ae7ddfc5 [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;
77import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
Hyunsun Moon44aac662017-02-18 02:07:01 +090078import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Lieae12362018-04-10 18:48:32 +090079import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090080import 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;
82import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
83import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
84import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
85import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_SUBNET_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
222 MacAddress replyMac = gatewayIp(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
Daniel Park4d7f88b2018-09-19 19:03:38 +0900244 private boolean gatewayIp(IpAddress targetIp) {
245 return osNetworkService.subnets().stream()
246 .anyMatch(subnet -> subnet.getGateway().equals(targetIp.toString()));
247 }
248
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700249 /**
250 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900251 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700252 *
253 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900254 * @param osNetId openstack network id of the source instance port
255 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700256 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900257 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700258 checkNotNull(targetIp);
259
Hyunsun Moon44aac662017-02-18 02:07:01 +0900260 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
261 if (instPort != null) {
262 log.trace("Found MAC from host service for {}", targetIp);
263 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700264 } else {
265 return MacAddress.NONE;
266 }
267 }
268
Jian Lieae12362018-04-10 18:48:32 +0900269 /**
Daniel Park613ac372018-06-28 14:30:11 +0900270 * Installs flow rules which convert ARP request packet into ARP reply
271 * by adding a fake gateway MAC address as Source Hardware Address.
272 *
273 * @param osSubnet openstack subnet
274 * @param install flag which indicates whether to install rule or remove rule
275 */
276 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
277
Jian Li7f70bb72018-07-06 23:35:30 +0900278 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Daniel Park613ac372018-06-28 14:30:11 +0900279 String gateway = osSubnet.getGateway();
280
281 TrafficSelector selector = DefaultTrafficSelector.builder()
282 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
283 .matchArpOp(ARP.OP_REQUEST)
284 .matchArpTpa(Ip4Address.valueOf(gateway))
285 .build();
286
287 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
288 .setArpOp(ARP.OP_REPLY)
289 .setArpSha(MacAddress.valueOf(gatewayMac))
290 .setArpSpa(Ip4Address.valueOf(gateway))
291 .setOutput(PortNumber.IN_PORT)
292 .build();
293
294 if (osNode == null) {
295 osNodeService.completeNodes(COMPUTE).forEach(n ->
296 osFlowRuleService.setRule(
297 appId,
298 n.intgBridge(),
299 selector,
300 treatment,
301 PRIORITY_ARP_GATEWAY_RULE,
302 DHCP_ARP_TABLE,
303 install
304 )
305 );
306 } else {
307 osFlowRuleService.setRule(
308 appId,
309 osNode.intgBridge(),
310 selector,
311 treatment,
312 PRIORITY_ARP_GATEWAY_RULE,
313 DHCP_ARP_TABLE,
314 install
315 );
316 }
317
318 }
319 }
320
321 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900322 * Installs flow rules to match ARP request packets.
323 *
324 * @param port instance port
325 * @param install installation flag
326 */
327 private void setArpRequestRule(InstancePort port, boolean install) {
328 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
329
330 switch (type) {
331 case VXLAN:
332 setRemoteArpRequestRuleForVxlan(port, install);
333 break;
334 case VLAN:
335 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
336 // there is no need to add any flow rules to handle ARP request
337 break;
338 default:
339 break;
340 }
341 }
342
343 /**
344 * Installs flow rules to match ARP reply packets.
345 *
346 * @param port instance port
347 * @param install installation flag
348 */
349 private void setArpReplyRule(InstancePort port, boolean install) {
350 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
351
352 switch (type) {
353 case VXLAN:
354 setArpReplyRuleForVxlan(port, install);
355 break;
356 case VLAN:
357 setArpReplyRuleForVlan(port, install);
358 break;
359 default:
360 break;
361 }
362 }
363
364 /**
365 * Installs flow rules to match ARP request packets only for VxLAN.
366 *
367 * @param port instance port
368 * @param install installation flag
369 */
370 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
371
372 OpenstackNode localNode = osNodeService.node(port.deviceId());
373
374 TrafficSelector selector = DefaultTrafficSelector.builder()
375 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
376 .matchArpOp(ARP.OP_REQUEST)
377 .matchArpTpa(port.ipAddress().getIp4Address())
378 .build();
379
380 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
381 }
382
383 /**
384 * Installs flow rules to match ARP reply packets only for VxLAN.
385 *
386 * @param port instance port
387 * @param install installation flag
388 */
389 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
390
391 OpenstackNode localNode = osNodeService.node(port.deviceId());
392
393 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
394 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
395 }
396
397 /**
398 * Installs flow rules to match ARP reply packets only for VLAN.
399 *
400 * @param port instance port
401 * @param install installation flag
402 */
403 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
404
405 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
406 setRemoteArpTreatmentForVlan(selector, port, install);
407 }
408
409 // a helper method
410 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
411 TrafficSelector selector = DefaultTrafficSelector.builder()
412 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
413 .matchArpOp(ARP.OP_REPLY)
414 .matchArpTpa(port.ipAddress().getIp4Address())
415 .matchArpTha(port.macAddress())
416 .build();
417
418 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
419 .setOutput(port.portNumber())
420 .build();
421
422 osFlowRuleService.setRule(
423 appId,
424 port.deviceId(),
425 selector,
426 treatment,
427 PRIORITY_ARP_REPLY_RULE,
428 DHCP_ARP_TABLE,
429 install
430 );
431
432 return selector;
433 }
434
435 // a helper method
436 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
437 InstancePort port,
438 OpenstackNode localNode,
439 boolean install) {
440 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
441 if (!remoteNode.intgBridge().equals(port.deviceId())) {
442 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
443 .extension(buildExtension(
444 deviceService,
445 remoteNode.intgBridge(),
446 localNode.dataIp().getIp4Address()),
447 remoteNode.intgBridge())
448 .setOutput(remoteNode.tunnelPortNum())
449 .build();
450
451 osFlowRuleService.setRule(
452 appId,
453 remoteNode.intgBridge(),
454 selector,
455 treatmentToRemote,
456 PRIORITY_ARP_REQUEST_RULE,
457 DHCP_ARP_TABLE,
458 install
459 );
460 }
461 }
462 }
463
464 // a helper method
465 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
466 InstancePort port,
467 boolean install) {
468 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
469 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
470 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
471 .setOutput(remoteNode.vlanPortNum())
472 .build();
473
474 osFlowRuleService.setRule(
475 appId,
476 remoteNode.intgBridge(),
477 selector,
478 treatmentToRemote,
479 PRIORITY_ARP_REQUEST_RULE,
480 DHCP_ARP_TABLE,
481 install);
482 }
483 }
484 }
485
486 /**
487 * Extracts properties from the component configuration context.
488 *
489 * @param context the component context
490 */
491 private void readComponentConfiguration(ComponentContext context) {
492 Dictionary<?, ?> properties = context.getProperties();
493
494 String updatedMac = Tools.get(properties, GATEWAY_MAC);
495 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
496 log.info("Configured. Gateway MAC is {}", gatewayMac);
497 }
498
499 /**
Jian Lieae12362018-04-10 18:48:32 +0900500 * An internal packet processor which processes ARP request, and results in
501 * packet-out ARP reply.
502 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700503 private class InternalPacketProcessor implements PacketProcessor {
504
505 @Override
506 public void process(PacketContext context) {
507 if (context.isHandled()) {
508 return;
509 }
510
511 Ethernet ethPacket = context.inPacket().parsed();
512 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
513 return;
514 }
515 processPacketIn(context, ethPacket);
516 }
517 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900518
Jian Lieae12362018-04-10 18:48:32 +0900519 /**
520 * An internal network listener which listens to openstack network event,
521 * manages the gateway collection and installs flow rule that handles
522 * ARP request in data plane.
523 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900524 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
525
526 @Override
527 public boolean isRelevant(OpenstackNetworkEvent event) {
528 Subnet osSubnet = event.subnet();
529 if (osSubnet == null) {
530 return false;
531 }
Jian Lieae12362018-04-10 18:48:32 +0900532
Jian Libb4f5412018-04-12 09:48:50 +0900533 Network network = osNetworkService.network(osSubnet.getNetworkId());
534
Jian Lieae12362018-04-10 18:48:32 +0900535 if (network == null) {
536 log.warn("Network is not specified.");
537 return false;
538 } else {
539 if (network.getNetworkType().equals(NetworkType.FLAT)) {
540 return false;
541 }
542 }
543
544 // do not allow to proceed without leadership
545 NodeId leader = leadershipService.getLeader(appId.name());
546 if (!Objects.equals(localNodeId, leader)) {
547 return false;
548 }
549
Hyunsun Moon44aac662017-02-18 02:07:01 +0900550 return !Strings.isNullOrEmpty(osSubnet.getGateway());
551 }
552
553 @Override
554 public void event(OpenstackNetworkEvent event) {
555 switch (event.type()) {
556 case OPENSTACK_SUBNET_CREATED:
557 case OPENSTACK_SUBNET_UPDATED:
Daniel Park613ac372018-06-28 14:30:11 +0900558 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900559 break;
560 case OPENSTACK_SUBNET_REMOVED:
Daniel Park613ac372018-06-28 14:30:11 +0900561 setFakeGatewayArpRule(event.subnet(), false, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900562 break;
563 case OPENSTACK_NETWORK_CREATED:
564 case OPENSTACK_NETWORK_UPDATED:
565 case OPENSTACK_NETWORK_REMOVED:
566 case OPENSTACK_PORT_CREATED:
567 case OPENSTACK_PORT_UPDATED:
568 case OPENSTACK_PORT_REMOVED:
569 default:
570 // do nothing for the other events
571 break;
572 }
573 }
Jian Lieae12362018-04-10 18:48:32 +0900574 }
575
576 /**
577 * An internal openstack node listener which is used for listening openstack
578 * node activity. As long as a node is in complete state, we will install
579 * default ARP rule to handle ARP request.
580 */
581 private class InternalNodeEventListener implements OpenstackNodeListener {
582
583 @Override
584 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900585
Jian Lieae12362018-04-10 18:48:32 +0900586 // do not allow to proceed without leadership
587 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900588 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900589 }
590
591 @Override
592 public void event(OpenstackNodeEvent event) {
593 OpenstackNode osNode = event.subject();
594 switch (event.type()) {
595 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900596 setDefaultArpRule(osNode, true);
Jian Li5b66ce02018-07-09 22:43:54 +0900597 setAllArpRules(osNode, true);
Jian Lieae12362018-04-10 18:48:32 +0900598 break;
599 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900600 setDefaultArpRule(osNode, false);
Jian Li5b66ce02018-07-09 22:43:54 +0900601 setAllArpRules(osNode, false);
Jian Lieae12362018-04-10 18:48:32 +0900602 break;
Jian Lieae12362018-04-10 18:48:32 +0900603 default:
604 break;
605 }
606 }
607
Jian Lif96685c2018-05-21 14:14:16 +0900608 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Libcc42282018-09-13 20:59:34 +0900609
610 if (getArpMode() == null) {
611 return;
612 }
613
Jian Li7f70bb72018-07-06 23:35:30 +0900614 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900615 case ARP_PROXY_MODE:
616 setDefaultArpRuleForProxyMode(osNode, install);
617 break;
618 case ARP_BROADCAST_MODE:
619 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900620
621 // we do not add fake gateway ARP rules for FLAT network
622 // ARP packets generated by FLAT typed VM should not be
623 // delegated to switch to handle
624 osNetworkService.subnets().stream().filter(subnet ->
625 osNetworkService.network(subnet.getNetworkId()) != null &&
626 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900627 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900628 .forEach(subnet ->
629 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900630 break;
631 default:
632 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900633 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900634 break;
Jian Lieae12362018-04-10 18:48:32 +0900635 }
636 }
Jian Lif96685c2018-05-21 14:14:16 +0900637
638 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
639 TrafficSelector selector = DefaultTrafficSelector.builder()
640 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
641 .build();
642
643 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
644 .punt()
645 .build();
646
647 osFlowRuleService.setRule(
648 appId,
649 osNode.intgBridge(),
650 selector,
651 treatment,
652 PRIORITY_ARP_CONTROL_RULE,
653 DHCP_ARP_TABLE,
654 install
655 );
656 }
657
658 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
659 TrafficSelector selector = DefaultTrafficSelector.builder()
660 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
661 .matchArpOp(ARP.OP_REQUEST)
662 .build();
663
664 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
665 .setOutput(PortNumber.FLOOD)
666 .build();
667
668 osFlowRuleService.setRule(
669 appId,
670 osNode.intgBridge(),
671 selector,
672 treatment,
673 PRIORITY_ARP_SUBNET_RULE,
674 DHCP_ARP_TABLE,
675 install
676 );
677 }
Jian Li7f70bb72018-07-06 23:35:30 +0900678
Jian Li5b66ce02018-07-09 22:43:54 +0900679 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900680 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900681 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900682 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900683 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
684 .forEach(p -> {
685 setArpRequestRule(p, install);
686 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900687 });
688 }
689 }
Jian Lieae12362018-04-10 18:48:32 +0900690 }
691
692 /**
693 * An internal instance port listener which listens the port events generated
694 * from VM. When ARP a host which located in a remote compute node, we specify
695 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
696 * host IP address. When ARP a host which located in a local compute node,
697 * we specify only ARP OP mode as REQUEST.
698 */
699 private class InternalInstancePortListener implements InstancePortListener {
700
701 @Override
702 public boolean isRelevant(InstancePortEvent event) {
703
Jian Li7f70bb72018-07-06 23:35:30 +0900704 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900705 return false;
706 }
707
708 InstancePort instPort = event.subject();
709 return mastershipService.isLocalMaster(instPort.deviceId());
710 }
711
712 @Override
713 public void event(InstancePortEvent event) {
714 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900715 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900716 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lieae12362018-04-10 18:48:32 +0900717 setArpRequestRule(event.subject(), true);
718 setArpReplyRule(event.subject(), true);
719 break;
720 case OPENSTACK_INSTANCE_PORT_VANISHED:
721 setArpRequestRule(event.subject(), false);
722 setArpReplyRule(event.subject(), false);
723 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900724 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
725 setArpRequestRule(event.subject(), true);
726 setArpReplyRule(event.subject(), true);
727 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900728 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900729 InstancePort revisedInstPort = swapStaleLocation(event.subject());
730 setArpRequestRule(revisedInstPort, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900731 break;
Jian Lieae12362018-04-10 18:48:32 +0900732 default:
733 break;
734 }
735 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900736 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700737}