blob: 3131263ee0b686b99c6badc1d456ae723caadd75 [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 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 private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
152
153 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900154 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700155
156 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800157 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900158 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
159 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900160 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900161 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900162 osNodeService.addListener(osNodeListener);
163 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700164 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900165
166 instancePortService.addListener(instancePortListener);
167
168 osNetworkService.networks().forEach(n -> {
169 if (n.getNetworkType() != NetworkType.FLAT) {
170 osNetworkService.subnets().forEach(s -> {
171 if (s.getNetworkId().equals(n.getId())) {
172 addSubnetGateway(s);
173 }
174 });
175 }
176 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900177
178 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700179 }
180
181 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800182 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700183 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900185 osNodeService.removeListener(osNodeListener);
186 instancePortService.removeListener(instancePortListener);
187 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 configService.unregisterProperties(getClass(), false);
189
190 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700191 }
192
193 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800194 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900195 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900196
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700197 log.info("Modified");
198 }
199
Jian Li7f70bb72018-07-06 23:35:30 +0900200 private String getArpMode() {
201 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
202 return getPropertyValue(properties, ARP_MODE);
203 }
204
Hyunsun Moon44aac662017-02-18 02:07:01 +0900205 private void addSubnetGateway(Subnet osSubnet) {
206 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
207 return;
208 }
209 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
210 gateways.add(gatewayIp);
211 log.debug("Added ARP proxy entry IP:{}", gatewayIp);
212 }
213
214 private void removeSubnetGateway(Subnet osSubnet) {
215 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
216 return;
217 }
218 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
219 gateways.remove(gatewayIp);
220 log.debug("Removed ARP proxy entry IP:{}", gatewayIp);
221 }
222
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700223 /**
224 * Processes ARP request packets.
225 * It checks if the target IP is owned by a known host first and then ask to
226 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
227 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900228 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700229 * @param ethPacket ethernet packet
230 */
231 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900232
233 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900234 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900235 return;
236 }
237
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700238 ARP arpPacket = (ARP) ethPacket.getPayload();
239 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
240 return;
241 }
242
Hyunsun Moon44aac662017-02-18 02:07:01 +0900243 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
244 if (srcInstPort == null) {
245 log.trace("Failed to find source instance port(MAC:{})",
246 ethPacket.getSourceMAC());
247 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700248 }
249
Hyunsun Moon44aac662017-02-18 02:07:01 +0900250 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
251 MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
252 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700253 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900254 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700255 return;
256 }
257
258 Ethernet ethReply = ARP.buildArpReply(
259 targetIp.getIp4Address(),
260 replyMac,
261 ethPacket);
262
263 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
264 .setOutput(context.inPacket().receivedFrom().port())
265 .build();
266
267 packetService.emit(new DefaultOutboundPacket(
268 context.inPacket().receivedFrom().deviceId(),
269 treatment,
270 ByteBuffer.wrap(ethReply.serialize())));
271 }
272
273 /**
274 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900275 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700276 *
277 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900278 * @param osNetId openstack network id of the source instance port
279 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700280 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900281 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700282 checkNotNull(targetIp);
283
Hyunsun Moon44aac662017-02-18 02:07:01 +0900284 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
285 if (instPort != null) {
286 log.trace("Found MAC from host service for {}", targetIp);
287 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700288 } else {
289 return MacAddress.NONE;
290 }
291 }
292
Jian Lieae12362018-04-10 18:48:32 +0900293 /**
Daniel Park613ac372018-06-28 14:30:11 +0900294 * Installs flow rules which convert ARP request packet into ARP reply
295 * by adding a fake gateway MAC address as Source Hardware Address.
296 *
297 * @param osSubnet openstack subnet
298 * @param install flag which indicates whether to install rule or remove rule
299 */
300 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
301
Jian Li7f70bb72018-07-06 23:35:30 +0900302 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Daniel Park613ac372018-06-28 14:30:11 +0900303 String gateway = osSubnet.getGateway();
304
305 TrafficSelector selector = DefaultTrafficSelector.builder()
306 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
307 .matchArpOp(ARP.OP_REQUEST)
308 .matchArpTpa(Ip4Address.valueOf(gateway))
309 .build();
310
311 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
312 .setArpOp(ARP.OP_REPLY)
313 .setArpSha(MacAddress.valueOf(gatewayMac))
314 .setArpSpa(Ip4Address.valueOf(gateway))
315 .setOutput(PortNumber.IN_PORT)
316 .build();
317
318 if (osNode == null) {
319 osNodeService.completeNodes(COMPUTE).forEach(n ->
320 osFlowRuleService.setRule(
321 appId,
322 n.intgBridge(),
323 selector,
324 treatment,
325 PRIORITY_ARP_GATEWAY_RULE,
326 DHCP_ARP_TABLE,
327 install
328 )
329 );
330 } else {
331 osFlowRuleService.setRule(
332 appId,
333 osNode.intgBridge(),
334 selector,
335 treatment,
336 PRIORITY_ARP_GATEWAY_RULE,
337 DHCP_ARP_TABLE,
338 install
339 );
340 }
341
342 }
343 }
344
345 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900346 * Installs flow rules to match ARP request packets.
347 *
348 * @param port instance port
349 * @param install installation flag
350 */
351 private void setArpRequestRule(InstancePort port, boolean install) {
352 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
353
354 switch (type) {
355 case VXLAN:
356 setRemoteArpRequestRuleForVxlan(port, install);
357 break;
358 case VLAN:
359 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
360 // there is no need to add any flow rules to handle ARP request
361 break;
362 default:
363 break;
364 }
365 }
366
367 /**
368 * Installs flow rules to match ARP reply packets.
369 *
370 * @param port instance port
371 * @param install installation flag
372 */
373 private void setArpReplyRule(InstancePort port, boolean install) {
374 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
375
376 switch (type) {
377 case VXLAN:
378 setArpReplyRuleForVxlan(port, install);
379 break;
380 case VLAN:
381 setArpReplyRuleForVlan(port, install);
382 break;
383 default:
384 break;
385 }
386 }
387
388 /**
389 * Installs flow rules to match ARP request packets only for VxLAN.
390 *
391 * @param port instance port
392 * @param install installation flag
393 */
394 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
395
396 OpenstackNode localNode = osNodeService.node(port.deviceId());
397
398 TrafficSelector selector = DefaultTrafficSelector.builder()
399 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
400 .matchArpOp(ARP.OP_REQUEST)
401 .matchArpTpa(port.ipAddress().getIp4Address())
402 .build();
403
404 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
405 }
406
407 /**
408 * Installs flow rules to match ARP reply packets only for VxLAN.
409 *
410 * @param port instance port
411 * @param install installation flag
412 */
413 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
414
415 OpenstackNode localNode = osNodeService.node(port.deviceId());
416
417 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
418 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
419 }
420
421 /**
422 * Installs flow rules to match ARP reply packets only for VLAN.
423 *
424 * @param port instance port
425 * @param install installation flag
426 */
427 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
428
429 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
430 setRemoteArpTreatmentForVlan(selector, port, install);
431 }
432
433 // a helper method
434 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
435 TrafficSelector selector = DefaultTrafficSelector.builder()
436 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
437 .matchArpOp(ARP.OP_REPLY)
438 .matchArpTpa(port.ipAddress().getIp4Address())
439 .matchArpTha(port.macAddress())
440 .build();
441
442 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
443 .setOutput(port.portNumber())
444 .build();
445
446 osFlowRuleService.setRule(
447 appId,
448 port.deviceId(),
449 selector,
450 treatment,
451 PRIORITY_ARP_REPLY_RULE,
452 DHCP_ARP_TABLE,
453 install
454 );
455
456 return selector;
457 }
458
459 // a helper method
460 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
461 InstancePort port,
462 OpenstackNode localNode,
463 boolean install) {
464 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
465 if (!remoteNode.intgBridge().equals(port.deviceId())) {
466 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
467 .extension(buildExtension(
468 deviceService,
469 remoteNode.intgBridge(),
470 localNode.dataIp().getIp4Address()),
471 remoteNode.intgBridge())
472 .setOutput(remoteNode.tunnelPortNum())
473 .build();
474
475 osFlowRuleService.setRule(
476 appId,
477 remoteNode.intgBridge(),
478 selector,
479 treatmentToRemote,
480 PRIORITY_ARP_REQUEST_RULE,
481 DHCP_ARP_TABLE,
482 install
483 );
484 }
485 }
486 }
487
488 // a helper method
489 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
490 InstancePort port,
491 boolean install) {
492 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
493 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
494 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
495 .setOutput(remoteNode.vlanPortNum())
496 .build();
497
498 osFlowRuleService.setRule(
499 appId,
500 remoteNode.intgBridge(),
501 selector,
502 treatmentToRemote,
503 PRIORITY_ARP_REQUEST_RULE,
504 DHCP_ARP_TABLE,
505 install);
506 }
507 }
508 }
509
510 /**
511 * Extracts properties from the component configuration context.
512 *
513 * @param context the component context
514 */
515 private void readComponentConfiguration(ComponentContext context) {
516 Dictionary<?, ?> properties = context.getProperties();
517
518 String updatedMac = Tools.get(properties, GATEWAY_MAC);
519 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
520 log.info("Configured. Gateway MAC is {}", gatewayMac);
521 }
522
523 /**
Jian Lieae12362018-04-10 18:48:32 +0900524 * An internal packet processor which processes ARP request, and results in
525 * packet-out ARP reply.
526 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700527 private class InternalPacketProcessor implements PacketProcessor {
528
529 @Override
530 public void process(PacketContext context) {
531 if (context.isHandled()) {
532 return;
533 }
534
535 Ethernet ethPacket = context.inPacket().parsed();
536 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
537 return;
538 }
539 processPacketIn(context, ethPacket);
540 }
541 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900542
Jian Lieae12362018-04-10 18:48:32 +0900543 /**
544 * An internal network listener which listens to openstack network event,
545 * manages the gateway collection and installs flow rule that handles
546 * ARP request in data plane.
547 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900548 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
549
550 @Override
551 public boolean isRelevant(OpenstackNetworkEvent event) {
552 Subnet osSubnet = event.subnet();
553 if (osSubnet == null) {
554 return false;
555 }
Jian Lieae12362018-04-10 18:48:32 +0900556
Jian Libb4f5412018-04-12 09:48:50 +0900557 Network network = osNetworkService.network(osSubnet.getNetworkId());
558
Jian Lieae12362018-04-10 18:48:32 +0900559 if (network == null) {
560 log.warn("Network is not specified.");
561 return false;
562 } else {
563 if (network.getNetworkType().equals(NetworkType.FLAT)) {
564 return false;
565 }
566 }
567
568 // do not allow to proceed without leadership
569 NodeId leader = leadershipService.getLeader(appId.name());
570 if (!Objects.equals(localNodeId, leader)) {
571 return false;
572 }
573
Hyunsun Moon44aac662017-02-18 02:07:01 +0900574 return !Strings.isNullOrEmpty(osSubnet.getGateway());
575 }
576
577 @Override
578 public void event(OpenstackNetworkEvent event) {
579 switch (event.type()) {
580 case OPENSTACK_SUBNET_CREATED:
581 case OPENSTACK_SUBNET_UPDATED:
582 addSubnetGateway(event.subnet());
Daniel Park613ac372018-06-28 14:30:11 +0900583 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900584 break;
585 case OPENSTACK_SUBNET_REMOVED:
586 removeSubnetGateway(event.subnet());
Daniel Park613ac372018-06-28 14:30:11 +0900587 setFakeGatewayArpRule(event.subnet(), false, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900588 break;
589 case OPENSTACK_NETWORK_CREATED:
590 case OPENSTACK_NETWORK_UPDATED:
591 case OPENSTACK_NETWORK_REMOVED:
592 case OPENSTACK_PORT_CREATED:
593 case OPENSTACK_PORT_UPDATED:
594 case OPENSTACK_PORT_REMOVED:
595 default:
596 // do nothing for the other events
597 break;
598 }
599 }
Jian Lieae12362018-04-10 18:48:32 +0900600 }
601
602 /**
603 * An internal openstack node listener which is used for listening openstack
604 * node activity. As long as a node is in complete state, we will install
605 * default ARP rule to handle ARP request.
606 */
607 private class InternalNodeEventListener implements OpenstackNodeListener {
608
609 @Override
610 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Lif7934d52018-07-10 16:27:02 +0900611
612 // add subnet gateway to local storage in all cluster nodes
613 // TODO: need to persistent the gateway collection into eventually
614 // consistent map sooner or later
615 addAllSubnetGateways();
616
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 Li5b66ce02018-07-09 22:43:54 +0900639 private void addAllSubnetGateways() {
640 osNetworkService.networks().forEach(n -> {
641 if (n.getNetworkType() != NetworkType.FLAT) {
642 osNetworkService.subnets()
643 .stream()
644 .filter(s -> s.getNetworkId().equals(n.getId()))
645 .filter(s -> !Strings.isNullOrEmpty(s.getGateway()))
Ray Milkeydd50eac2018-07-09 12:57:56 -0700646 .filter(s -> !gateways.contains(IpAddress.valueOf(s.getGateway())))
Jian Li5b66ce02018-07-09 22:43:54 +0900647 .forEach(OpenstackSwitchingArpHandler.this::addSubnetGateway);
648 }
649 });
650 }
651
Jian Lif96685c2018-05-21 14:14:16 +0900652 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900653 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900654 case ARP_PROXY_MODE:
655 setDefaultArpRuleForProxyMode(osNode, install);
656 break;
657 case ARP_BROADCAST_MODE:
658 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900659
660 // we do not add fake gateway ARP rules for FLAT network
661 // ARP packets generated by FLAT typed VM should not be
662 // delegated to switch to handle
663 osNetworkService.subnets().stream().filter(subnet ->
664 osNetworkService.network(subnet.getNetworkId()) != null &&
665 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900666 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900667 .forEach(subnet ->
668 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900669 break;
670 default:
671 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900672 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900673 break;
Jian Lieae12362018-04-10 18:48:32 +0900674 }
675 }
Jian Lif96685c2018-05-21 14:14:16 +0900676
677 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
678 TrafficSelector selector = DefaultTrafficSelector.builder()
679 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
680 .build();
681
682 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
683 .punt()
684 .build();
685
686 osFlowRuleService.setRule(
687 appId,
688 osNode.intgBridge(),
689 selector,
690 treatment,
691 PRIORITY_ARP_CONTROL_RULE,
692 DHCP_ARP_TABLE,
693 install
694 );
695 }
696
697 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
698 TrafficSelector selector = DefaultTrafficSelector.builder()
699 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
700 .matchArpOp(ARP.OP_REQUEST)
701 .build();
702
703 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
704 .setOutput(PortNumber.FLOOD)
705 .build();
706
707 osFlowRuleService.setRule(
708 appId,
709 osNode.intgBridge(),
710 selector,
711 treatment,
712 PRIORITY_ARP_SUBNET_RULE,
713 DHCP_ARP_TABLE,
714 install
715 );
716 }
Jian Li7f70bb72018-07-06 23:35:30 +0900717
Jian Li5b66ce02018-07-09 22:43:54 +0900718 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900719 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900720 instancePortService.instancePorts().stream()
721 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
722 .forEach(p -> {
723 setArpRequestRule(p, install);
724 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900725 });
726 }
727 }
Jian Lieae12362018-04-10 18:48:32 +0900728 }
729
730 /**
731 * An internal instance port listener which listens the port events generated
732 * from VM. When ARP a host which located in a remote compute node, we specify
733 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
734 * host IP address. When ARP a host which located in a local compute node,
735 * we specify only ARP OP mode as REQUEST.
736 */
737 private class InternalInstancePortListener implements InstancePortListener {
738
739 @Override
740 public boolean isRelevant(InstancePortEvent event) {
741
Jian Li7f70bb72018-07-06 23:35:30 +0900742 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900743 return false;
744 }
745
746 InstancePort instPort = event.subject();
747 return mastershipService.isLocalMaster(instPort.deviceId());
748 }
749
750 @Override
751 public void event(InstancePortEvent event) {
752 switch (event.type()) {
Jian Lieae12362018-04-10 18:48:32 +0900753 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900754 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Lieae12362018-04-10 18:48:32 +0900755 setArpRequestRule(event.subject(), true);
756 setArpReplyRule(event.subject(), true);
757 break;
758 case OPENSTACK_INSTANCE_PORT_VANISHED:
759 setArpRequestRule(event.subject(), false);
760 setArpReplyRule(event.subject(), false);
761 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900762 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
763 setArpRequestRule(event.subject(), true);
764 setArpReplyRule(event.subject(), true);
765 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900766 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900767 InstancePort revisedInstPort = swapStaleLocation(event.subject());
768 setArpRequestRule(revisedInstPort, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900769 break;
Jian Lieae12362018-04-10 18:48:32 +0900770 default:
771 break;
772 }
773 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900774 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700775}