blob: 59241cb7a5d9bef84dd098fa5faca9554aa9498c [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;
19import com.google.common.collect.Sets;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.ARP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090028import org.onlab.packet.EthType;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070029import org.onlab.packet.Ethernet;
30import org.onlab.packet.Ip4Address;
31import org.onlab.packet.IpAddress;
32import org.onlab.packet.MacAddress;
33import org.onlab.util.Tools;
Hyunsun Moon44aac662017-02-18 02:07:01 +090034import org.onosproject.cfg.ComponentConfigService;
Jian Li7f70bb72018-07-06 23:35:30 +090035import org.onosproject.cfg.ConfigProperty;
Jian Lieae12362018-04-10 18:48:32 +090036import org.onosproject.cluster.ClusterService;
37import org.onosproject.cluster.LeadershipService;
38import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090039import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
Jian Lieae12362018-04-10 18:48:32 +090041import org.onosproject.mastership.MastershipService;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.device.DeviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.net.flow.DefaultTrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070045import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.net.flow.TrafficSelector;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070047import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.packet.DefaultOutboundPacket;
49import org.onosproject.net.packet.PacketContext;
50import org.onosproject.net.packet.PacketProcessor;
51import org.onosproject.net.packet.PacketService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.onosproject.openstacknetworking.api.InstancePort;
Jian Lieae12362018-04-10 18:48:32 +090053import org.onosproject.openstacknetworking.api.InstancePortEvent;
54import org.onosproject.openstacknetworking.api.InstancePortListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090055import org.onosproject.openstacknetworking.api.InstancePortService;
Jian Lieae12362018-04-10 18:48:32 +090056import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090057import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
58import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
59import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lieae12362018-04-10 18:48:32 +090060import org.onosproject.openstacknode.api.OpenstackNode;
61import org.onosproject.openstacknode.api.OpenstackNodeEvent;
62import org.onosproject.openstacknode.api.OpenstackNodeListener;
63import org.onosproject.openstacknode.api.OpenstackNodeService;
64import org.openstack4j.model.network.Network;
65import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090066import org.openstack4j.model.network.Subnet;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070067import org.osgi.service.component.ComponentContext;
68import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
Hyunsun Moon44aac662017-02-18 02:07:01 +090070
Hyunsun Moonb974fca2016-06-30 21:20:39 -070071import java.nio.ByteBuffer;
72import java.util.Dictionary;
Jian Lieae12362018-04-10 18:48:32 +090073import java.util.Objects;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070074import java.util.Set;
75
76import static com.google.common.base.Preconditions.checkNotNull;
Jian Lieae12362018-04-10 18:48:32 +090077import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
78import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
79import 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;
Jian Lieae12362018-04-10 18:48:32 +090081import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090082import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lieae12362018-04-10 18:48:32 +090083import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
84import 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;
87import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_SUBNET_RULE;
Jian Lic2403592018-07-18 12:56:45 +090088import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Li7f70bb72018-07-06 23:35:30 +090089import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090090import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Lieae12362018-04-10 18:48:32 +090091import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
92import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070093
94/**
95 * Handles ARP packet from VMs.
96 */
97@Component(immediate = true)
Hyunsun Moon44aac662017-02-18 02:07:01 +090098public final class OpenstackSwitchingArpHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -070099
100 private final Logger log = LoggerFactory.getLogger(getClass());
101
102 private static final String GATEWAY_MAC = "gatewayMac";
Jian Lieae12362018-04-10 18:48:32 +0900103 private static final String ARP_MODE = "arpMode";
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800106 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800109 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900112 OpenstackFlowRuleService osFlowRuleService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800115 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900118 ClusterService clusterService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 LeadershipService leadershipService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 DeviceService deviceService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 MastershipService mastershipService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800130 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800133 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700134
Jian Lieae12362018-04-10 18:48:32 +0900135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected OpenstackNodeService osNodeService;
137
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700138 @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700139 label = "Fake MAC address for virtual network subnet gateway")
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700140 private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700141
Jian Lieae12362018-04-10 18:48:32 +0900142 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900143 label = "ARP processing mode, broadcast | proxy (default)")
Jian Lieae12362018-04-10 18:48:32 +0900144 protected String arpMode = DEFAULT_ARP_MODE_STR;
145
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700146 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900147 private final InternalOpenstackNetworkListener osNetworkListener =
148 new InternalOpenstackNetworkListener();
Jian Lieae12362018-04-10 18:48:32 +0900149 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
150 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
151
Hyunsun Moon44aac662017-02-18 02:07:01 +0900152 private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
153
154 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900155 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700156
157 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800158 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900159 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
160 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900161 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900162 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900163 osNodeService.addListener(osNodeListener);
164 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700165 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900166
167 instancePortService.addListener(instancePortListener);
168
169 osNetworkService.networks().forEach(n -> {
170 if (n.getNetworkType() != NetworkType.FLAT) {
171 osNetworkService.subnets().forEach(s -> {
172 if (s.getNetworkId().equals(n.getId())) {
173 addSubnetGateway(s);
174 }
175 });
176 }
177 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900178
179 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700180 }
181
182 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800183 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700184 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900185 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900186 osNodeService.removeListener(osNodeListener);
187 instancePortService.removeListener(instancePortListener);
188 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900189 configService.unregisterProperties(getClass(), false);
190
191 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700192 }
193
194 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800195 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900196 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900197
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700198 log.info("Modified");
199 }
200
Jian Li7f70bb72018-07-06 23:35:30 +0900201 private String getArpMode() {
202 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
203 return getPropertyValue(properties, ARP_MODE);
204 }
205
Hyunsun Moon44aac662017-02-18 02:07:01 +0900206 private void addSubnetGateway(Subnet osSubnet) {
207 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
208 return;
209 }
210 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
211 gateways.add(gatewayIp);
212 log.debug("Added ARP proxy entry IP:{}", gatewayIp);
213 }
214
215 private void removeSubnetGateway(Subnet osSubnet) {
216 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
217 return;
218 }
219 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
220 gateways.remove(gatewayIp);
221 log.debug("Removed ARP proxy entry IP:{}", gatewayIp);
222 }
223
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700224 /**
225 * Processes ARP request packets.
226 * It checks if the target IP is owned by a known host first and then ask to
227 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
228 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900229 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700230 * @param ethPacket ethernet packet
231 */
232 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900233
234 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900235 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900236 return;
237 }
238
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700239 ARP arpPacket = (ARP) ethPacket.getPayload();
240 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
241 return;
242 }
243
Hyunsun Moon44aac662017-02-18 02:07:01 +0900244 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
245 if (srcInstPort == null) {
246 log.trace("Failed to find source instance port(MAC:{})",
247 ethPacket.getSourceMAC());
248 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700249 }
250
Hyunsun Moon44aac662017-02-18 02:07:01 +0900251 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
252 MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
253 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700254 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900255 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700256 return;
257 }
258
259 Ethernet ethReply = ARP.buildArpReply(
260 targetIp.getIp4Address(),
261 replyMac,
262 ethPacket);
263
264 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
265 .setOutput(context.inPacket().receivedFrom().port())
266 .build();
267
268 packetService.emit(new DefaultOutboundPacket(
269 context.inPacket().receivedFrom().deviceId(),
270 treatment,
271 ByteBuffer.wrap(ethReply.serialize())));
272 }
273
274 /**
275 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900276 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700277 *
278 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900279 * @param osNetId openstack network id of the source instance port
280 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700281 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900282 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700283 checkNotNull(targetIp);
284
Hyunsun Moon44aac662017-02-18 02:07:01 +0900285 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
286 if (instPort != null) {
287 log.trace("Found MAC from host service for {}", targetIp);
288 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700289 } else {
290 return MacAddress.NONE;
291 }
292 }
293
Jian Lieae12362018-04-10 18:48:32 +0900294 /**
Daniel Park613ac372018-06-28 14:30:11 +0900295 * Installs flow rules which convert ARP request packet into ARP reply
296 * by adding a fake gateway MAC address as Source Hardware Address.
297 *
298 * @param osSubnet openstack subnet
299 * @param install flag which indicates whether to install rule or remove rule
300 */
301 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
302
Jian Li7f70bb72018-07-06 23:35:30 +0900303 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Daniel Park613ac372018-06-28 14:30:11 +0900304 String gateway = osSubnet.getGateway();
305
306 TrafficSelector selector = DefaultTrafficSelector.builder()
307 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
308 .matchArpOp(ARP.OP_REQUEST)
309 .matchArpTpa(Ip4Address.valueOf(gateway))
310 .build();
311
312 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
313 .setArpOp(ARP.OP_REPLY)
314 .setArpSha(MacAddress.valueOf(gatewayMac))
315 .setArpSpa(Ip4Address.valueOf(gateway))
316 .setOutput(PortNumber.IN_PORT)
317 .build();
318
319 if (osNode == null) {
320 osNodeService.completeNodes(COMPUTE).forEach(n ->
321 osFlowRuleService.setRule(
322 appId,
323 n.intgBridge(),
324 selector,
325 treatment,
326 PRIORITY_ARP_GATEWAY_RULE,
327 DHCP_ARP_TABLE,
328 install
329 )
330 );
331 } else {
332 osFlowRuleService.setRule(
333 appId,
334 osNode.intgBridge(),
335 selector,
336 treatment,
337 PRIORITY_ARP_GATEWAY_RULE,
338 DHCP_ARP_TABLE,
339 install
340 );
341 }
342
343 }
344 }
345
346 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900347 * Installs flow rules to match ARP request packets.
348 *
349 * @param port instance port
350 * @param install installation flag
351 */
352 private void setArpRequestRule(InstancePort port, boolean install) {
353 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
354
355 switch (type) {
356 case VXLAN:
357 setRemoteArpRequestRuleForVxlan(port, install);
358 break;
359 case VLAN:
360 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
361 // there is no need to add any flow rules to handle ARP request
362 break;
363 default:
364 break;
365 }
366 }
367
368 /**
369 * Installs flow rules to match ARP reply packets.
370 *
371 * @param port instance port
372 * @param install installation flag
373 */
374 private void setArpReplyRule(InstancePort port, boolean install) {
375 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
376
377 switch (type) {
378 case VXLAN:
379 setArpReplyRuleForVxlan(port, install);
380 break;
381 case VLAN:
382 setArpReplyRuleForVlan(port, install);
383 break;
384 default:
385 break;
386 }
387 }
388
389 /**
390 * Installs flow rules to match ARP request packets only for VxLAN.
391 *
392 * @param port instance port
393 * @param install installation flag
394 */
395 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
396
397 OpenstackNode localNode = osNodeService.node(port.deviceId());
398
399 TrafficSelector selector = DefaultTrafficSelector.builder()
400 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
401 .matchArpOp(ARP.OP_REQUEST)
402 .matchArpTpa(port.ipAddress().getIp4Address())
403 .build();
404
405 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
406 }
407
408 /**
409 * Installs flow rules to match ARP reply packets only for VxLAN.
410 *
411 * @param port instance port
412 * @param install installation flag
413 */
414 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
415
416 OpenstackNode localNode = osNodeService.node(port.deviceId());
417
418 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
419 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
420 }
421
422 /**
423 * Installs flow rules to match ARP reply packets only for VLAN.
424 *
425 * @param port instance port
426 * @param install installation flag
427 */
428 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
429
430 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
431 setRemoteArpTreatmentForVlan(selector, port, install);
432 }
433
434 // a helper method
435 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
436 TrafficSelector selector = DefaultTrafficSelector.builder()
437 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
438 .matchArpOp(ARP.OP_REPLY)
439 .matchArpTpa(port.ipAddress().getIp4Address())
440 .matchArpTha(port.macAddress())
441 .build();
442
443 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
444 .setOutput(port.portNumber())
445 .build();
446
447 osFlowRuleService.setRule(
448 appId,
449 port.deviceId(),
450 selector,
451 treatment,
452 PRIORITY_ARP_REPLY_RULE,
453 DHCP_ARP_TABLE,
454 install
455 );
456
457 return selector;
458 }
459
460 // a helper method
461 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
462 InstancePort port,
463 OpenstackNode localNode,
464 boolean install) {
465 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
466 if (!remoteNode.intgBridge().equals(port.deviceId())) {
467 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
468 .extension(buildExtension(
469 deviceService,
470 remoteNode.intgBridge(),
471 localNode.dataIp().getIp4Address()),
472 remoteNode.intgBridge())
473 .setOutput(remoteNode.tunnelPortNum())
474 .build();
475
476 osFlowRuleService.setRule(
477 appId,
478 remoteNode.intgBridge(),
479 selector,
480 treatmentToRemote,
481 PRIORITY_ARP_REQUEST_RULE,
482 DHCP_ARP_TABLE,
483 install
484 );
485 }
486 }
487 }
488
489 // a helper method
490 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
491 InstancePort port,
492 boolean install) {
493 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
494 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
495 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
496 .setOutput(remoteNode.vlanPortNum())
497 .build();
498
499 osFlowRuleService.setRule(
500 appId,
501 remoteNode.intgBridge(),
502 selector,
503 treatmentToRemote,
504 PRIORITY_ARP_REQUEST_RULE,
505 DHCP_ARP_TABLE,
506 install);
507 }
508 }
509 }
510
511 /**
512 * Extracts properties from the component configuration context.
513 *
514 * @param context the component context
515 */
516 private void readComponentConfiguration(ComponentContext context) {
517 Dictionary<?, ?> properties = context.getProperties();
518
519 String updatedMac = Tools.get(properties, GATEWAY_MAC);
520 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
521 log.info("Configured. Gateway MAC is {}", gatewayMac);
522 }
523
524 /**
Jian Lieae12362018-04-10 18:48:32 +0900525 * An internal packet processor which processes ARP request, and results in
526 * packet-out ARP reply.
527 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700528 private class InternalPacketProcessor implements PacketProcessor {
529
530 @Override
531 public void process(PacketContext context) {
532 if (context.isHandled()) {
533 return;
534 }
535
536 Ethernet ethPacket = context.inPacket().parsed();
537 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
538 return;
539 }
540 processPacketIn(context, ethPacket);
541 }
542 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900543
Jian Lieae12362018-04-10 18:48:32 +0900544 /**
545 * An internal network listener which listens to openstack network event,
546 * manages the gateway collection and installs flow rule that handles
547 * ARP request in data plane.
548 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900549 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
550
551 @Override
552 public boolean isRelevant(OpenstackNetworkEvent event) {
553 Subnet osSubnet = event.subnet();
554 if (osSubnet == null) {
555 return false;
556 }
Jian Lieae12362018-04-10 18:48:32 +0900557
Jian Libb4f5412018-04-12 09:48:50 +0900558 Network network = osNetworkService.network(osSubnet.getNetworkId());
559
Jian Lieae12362018-04-10 18:48:32 +0900560 if (network == null) {
561 log.warn("Network is not specified.");
562 return false;
563 } else {
564 if (network.getNetworkType().equals(NetworkType.FLAT)) {
565 return false;
566 }
567 }
568
569 // do not allow to proceed without leadership
570 NodeId leader = leadershipService.getLeader(appId.name());
571 if (!Objects.equals(localNodeId, leader)) {
572 return false;
573 }
574
Hyunsun Moon44aac662017-02-18 02:07:01 +0900575 return !Strings.isNullOrEmpty(osSubnet.getGateway());
576 }
577
578 @Override
579 public void event(OpenstackNetworkEvent event) {
580 switch (event.type()) {
581 case OPENSTACK_SUBNET_CREATED:
582 case OPENSTACK_SUBNET_UPDATED:
583 addSubnetGateway(event.subnet());
Daniel Park613ac372018-06-28 14:30:11 +0900584 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900585 break;
586 case OPENSTACK_SUBNET_REMOVED:
587 removeSubnetGateway(event.subnet());
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
613 // add subnet gateway to local storage in all cluster nodes
614 // TODO: need to persistent the gateway collection into eventually
615 // consistent map sooner or later
616 addAllSubnetGateways();
617
Jian Lieae12362018-04-10 18:48:32 +0900618 // do not allow to proceed without leadership
619 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900620 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900621 }
622
623 @Override
624 public void event(OpenstackNodeEvent event) {
625 OpenstackNode osNode = event.subject();
626 switch (event.type()) {
627 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900628 setDefaultArpRule(osNode, true);
Jian Li5b66ce02018-07-09 22:43:54 +0900629 setAllArpRules(osNode, true);
Jian Lieae12362018-04-10 18:48:32 +0900630 break;
631 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900632 setDefaultArpRule(osNode, false);
Jian Li5b66ce02018-07-09 22:43:54 +0900633 setAllArpRules(osNode, false);
Jian Lieae12362018-04-10 18:48:32 +0900634 break;
Jian Lieae12362018-04-10 18:48:32 +0900635 default:
636 break;
637 }
638 }
639
Jian Li5b66ce02018-07-09 22:43:54 +0900640 private void addAllSubnetGateways() {
641 osNetworkService.networks().forEach(n -> {
642 if (n.getNetworkType() != NetworkType.FLAT) {
643 osNetworkService.subnets()
644 .stream()
645 .filter(s -> s.getNetworkId().equals(n.getId()))
646 .filter(s -> !Strings.isNullOrEmpty(s.getGateway()))
Ray Milkeydd50eac2018-07-09 12:57:56 -0700647 .filter(s -> !gateways.contains(IpAddress.valueOf(s.getGateway())))
Jian Li5b66ce02018-07-09 22:43:54 +0900648 .forEach(OpenstackSwitchingArpHandler.this::addSubnetGateway);
649 }
650 });
651 }
652
Jian Lif96685c2018-05-21 14:14:16 +0900653 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900654 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900655 case ARP_PROXY_MODE:
656 setDefaultArpRuleForProxyMode(osNode, install);
657 break;
658 case ARP_BROADCAST_MODE:
659 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900660
661 // we do not add fake gateway ARP rules for FLAT network
662 // ARP packets generated by FLAT typed VM should not be
663 // delegated to switch to handle
664 osNetworkService.subnets().stream().filter(subnet ->
665 osNetworkService.network(subnet.getNetworkId()) != null &&
666 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900667 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900668 .forEach(subnet ->
669 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900670 break;
671 default:
672 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900673 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900674 break;
Jian Lieae12362018-04-10 18:48:32 +0900675 }
676 }
Jian Lif96685c2018-05-21 14:14:16 +0900677
678 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
679 TrafficSelector selector = DefaultTrafficSelector.builder()
680 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
681 .build();
682
683 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
684 .punt()
685 .build();
686
687 osFlowRuleService.setRule(
688 appId,
689 osNode.intgBridge(),
690 selector,
691 treatment,
692 PRIORITY_ARP_CONTROL_RULE,
693 DHCP_ARP_TABLE,
694 install
695 );
696 }
697
698 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
699 TrafficSelector selector = DefaultTrafficSelector.builder()
700 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
701 .matchArpOp(ARP.OP_REQUEST)
702 .build();
703
704 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
705 .setOutput(PortNumber.FLOOD)
706 .build();
707
708 osFlowRuleService.setRule(
709 appId,
710 osNode.intgBridge(),
711 selector,
712 treatment,
713 PRIORITY_ARP_SUBNET_RULE,
714 DHCP_ARP_TABLE,
715 install
716 );
717 }
Jian Li7f70bb72018-07-06 23:35:30 +0900718
Jian Li5b66ce02018-07-09 22:43:54 +0900719 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900720 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900721 instancePortService.instancePorts().stream()
Jian Lic2403592018-07-18 12:56:45 +0900722 .filter(p -> p.state() == ACTIVE)
Jian Li5b66ce02018-07-09 22:43:54 +0900723 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
724 .forEach(p -> {
725 setArpRequestRule(p, install);
726 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900727 });
728 }
729 }
Jian Lieae12362018-04-10 18:48:32 +0900730 }
731
732 /**
733 * An internal instance port listener which listens the port events generated
734 * from VM. When ARP a host which located in a remote compute node, we specify
735 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
736 * host IP address. When ARP a host which located in a local compute node,
737 * we specify only ARP OP mode as REQUEST.
738 */
739 private class InternalInstancePortListener implements InstancePortListener {
740
741 @Override
742 public boolean isRelevant(InstancePortEvent event) {
743
Jian Li7f70bb72018-07-06 23:35:30 +0900744 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900745 return false;
746 }
747
748 InstancePort instPort = event.subject();
749 return mastershipService.isLocalMaster(instPort.deviceId());
750 }
751
752 @Override
753 public void event(InstancePortEvent event) {
754 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900755 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900756 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lieae12362018-04-10 18:48:32 +0900757 setArpRequestRule(event.subject(), true);
758 setArpReplyRule(event.subject(), true);
759 break;
760 case OPENSTACK_INSTANCE_PORT_VANISHED:
761 setArpRequestRule(event.subject(), false);
762 setArpReplyRule(event.subject(), false);
763 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900764 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
765 setArpRequestRule(event.subject(), true);
766 setArpReplyRule(event.subject(), true);
767 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900768 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900769 InstancePort revisedInstPort = swapStaleLocation(event.subject());
770 setArpRequestRule(revisedInstPort, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900771 break;
Jian Lieae12362018-04-10 18:48:32 +0900772 default:
773 break;
774 }
775 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900776 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700777}