blob: 03700cfc5cb363385077818700dd4edf029be62d [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 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
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800104 CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800107 PacketService packetService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900110 OpenstackFlowRuleService osFlowRuleService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800113 ComponentConfigService configService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lieae12362018-04-10 18:48:32 +0900116 ClusterService clusterService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 LeadershipService leadershipService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 DeviceService deviceService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 MastershipService mastershipService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800128 InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ray Milkey9c9cde42018-01-12 14:22:06 -0800131 OpenstackNetworkService osNetworkService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700132
Jian Lieae12362018-04-10 18:48:32 +0900133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected OpenstackNodeService osNodeService;
135
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700136 @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700137 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
Jian Lieae12362018-04-10 18:48:32 +0900140 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
Daniel Park6041f102018-07-06 18:49:45 +0900141 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 private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
151
152 private ApplicationId appId;
Jian Lieae12362018-04-10 18:48:32 +0900153 private NodeId localNodeId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700154
155 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800156 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900157 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
158 configService.registerProperties(getClass());
Jian Lieae12362018-04-10 18:48:32 +0900159 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160 osNetworkService.addListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900161 osNodeService.addListener(osNodeListener);
162 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700163 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
Jian Lieae12362018-04-10 18:48:32 +0900164
165 instancePortService.addListener(instancePortListener);
166
167 osNetworkService.networks().forEach(n -> {
168 if (n.getNetworkType() != NetworkType.FLAT) {
169 osNetworkService.subnets().forEach(s -> {
170 if (s.getNetworkId().equals(n.getId())) {
171 addSubnetGateway(s);
172 }
173 });
174 }
175 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176
177 log.info("Started");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700178 }
179
180 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800181 void deactivate() {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700182 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900183 osNetworkService.removeListener(osNetworkListener);
Jian Lieae12362018-04-10 18:48:32 +0900184 osNodeService.removeListener(osNodeListener);
185 instancePortService.removeListener(instancePortListener);
186 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900187 configService.unregisterProperties(getClass(), false);
188
189 log.info("Stopped");
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700190 }
191
192 @Modified
Ray Milkey9c9cde42018-01-12 14:22:06 -0800193 void modified(ComponentContext context) {
Jian Li7f70bb72018-07-06 23:35:30 +0900194 readComponentConfiguration(context);
Jian Lieae12362018-04-10 18:48:32 +0900195
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700196 log.info("Modified");
197 }
198
Jian Li7f70bb72018-07-06 23:35:30 +0900199 private String getArpMode() {
200 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
201 return getPropertyValue(properties, ARP_MODE);
202 }
203
Hyunsun Moon44aac662017-02-18 02:07:01 +0900204 private void addSubnetGateway(Subnet osSubnet) {
205 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
206 return;
207 }
208 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
209 gateways.add(gatewayIp);
210 log.debug("Added ARP proxy entry IP:{}", gatewayIp);
211 }
212
213 private void removeSubnetGateway(Subnet osSubnet) {
214 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
215 return;
216 }
217 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
218 gateways.remove(gatewayIp);
219 log.debug("Removed ARP proxy entry IP:{}", gatewayIp);
220 }
221
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700222 /**
223 * Processes ARP request packets.
224 * It checks if the target IP is owned by a known host first and then ask to
225 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
226 *
Hyunsun Moon44aac662017-02-18 02:07:01 +0900227 * @param context packet context
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700228 * @param ethPacket ethernet packet
229 */
230 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
Jian Lieae12362018-04-10 18:48:32 +0900231
232 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
Jian Li7f70bb72018-07-06 23:35:30 +0900233 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900234 return;
235 }
236
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700237 ARP arpPacket = (ARP) ethPacket.getPayload();
238 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
239 return;
240 }
241
Hyunsun Moon44aac662017-02-18 02:07:01 +0900242 InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
243 if (srcInstPort == null) {
244 log.trace("Failed to find source instance port(MAC:{})",
245 ethPacket.getSourceMAC());
246 return;
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700247 }
248
Hyunsun Moon44aac662017-02-18 02:07:01 +0900249 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
250 MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
251 getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700252 if (replyMac == MacAddress.NONE) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900253 log.trace("Failed to find MAC address for {}", targetIp);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700254 return;
255 }
256
257 Ethernet ethReply = ARP.buildArpReply(
258 targetIp.getIp4Address(),
259 replyMac,
260 ethPacket);
261
262 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
263 .setOutput(context.inPacket().receivedFrom().port())
264 .build();
265
266 packetService.emit(new DefaultOutboundPacket(
267 context.inPacket().receivedFrom().deviceId(),
268 treatment,
269 ByteBuffer.wrap(ethReply.serialize())));
270 }
271
272 /**
273 * Returns MAC address of a host with a given target IP address by asking to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900274 * instance port service.
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700275 *
276 * @param targetIp target ip
Hyunsun Moon44aac662017-02-18 02:07:01 +0900277 * @param osNetId openstack network id of the source instance port
278 * @return mac address, or none mac address if it fails to find the mac
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700279 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900280 private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700281 checkNotNull(targetIp);
282
Hyunsun Moon44aac662017-02-18 02:07:01 +0900283 InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
284 if (instPort != null) {
285 log.trace("Found MAC from host service for {}", targetIp);
286 return instPort.macAddress();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700287 } else {
288 return MacAddress.NONE;
289 }
290 }
291
Jian Lieae12362018-04-10 18:48:32 +0900292 /**
Daniel Park613ac372018-06-28 14:30:11 +0900293 * Installs flow rules which convert ARP request packet into ARP reply
294 * by adding a fake gateway MAC address as Source Hardware Address.
295 *
296 * @param osSubnet openstack subnet
297 * @param install flag which indicates whether to install rule or remove rule
298 */
299 private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
300
Jian Li7f70bb72018-07-06 23:35:30 +0900301 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Daniel Park613ac372018-06-28 14:30:11 +0900302 String gateway = osSubnet.getGateway();
303
304 TrafficSelector selector = DefaultTrafficSelector.builder()
305 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
306 .matchArpOp(ARP.OP_REQUEST)
307 .matchArpTpa(Ip4Address.valueOf(gateway))
308 .build();
309
310 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
311 .setArpOp(ARP.OP_REPLY)
312 .setArpSha(MacAddress.valueOf(gatewayMac))
313 .setArpSpa(Ip4Address.valueOf(gateway))
314 .setOutput(PortNumber.IN_PORT)
315 .build();
316
317 if (osNode == null) {
318 osNodeService.completeNodes(COMPUTE).forEach(n ->
319 osFlowRuleService.setRule(
320 appId,
321 n.intgBridge(),
322 selector,
323 treatment,
324 PRIORITY_ARP_GATEWAY_RULE,
325 DHCP_ARP_TABLE,
326 install
327 )
328 );
329 } else {
330 osFlowRuleService.setRule(
331 appId,
332 osNode.intgBridge(),
333 selector,
334 treatment,
335 PRIORITY_ARP_GATEWAY_RULE,
336 DHCP_ARP_TABLE,
337 install
338 );
339 }
340
341 }
342 }
343
344 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900345 * Installs flow rules to match ARP request packets.
346 *
347 * @param port instance port
348 * @param install installation flag
349 */
350 private void setArpRequestRule(InstancePort port, boolean install) {
351 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
352
353 switch (type) {
354 case VXLAN:
355 setRemoteArpRequestRuleForVxlan(port, install);
356 break;
357 case VLAN:
358 // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
359 // there is no need to add any flow rules to handle ARP request
360 break;
361 default:
362 break;
363 }
364 }
365
366 /**
367 * Installs flow rules to match ARP reply packets.
368 *
369 * @param port instance port
370 * @param install installation flag
371 */
372 private void setArpReplyRule(InstancePort port, boolean install) {
373 NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
374
375 switch (type) {
376 case VXLAN:
377 setArpReplyRuleForVxlan(port, install);
378 break;
379 case VLAN:
380 setArpReplyRuleForVlan(port, install);
381 break;
382 default:
383 break;
384 }
385 }
386
387 /**
388 * Installs flow rules to match ARP request packets only for VxLAN.
389 *
390 * @param port instance port
391 * @param install installation flag
392 */
393 private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
394
395 OpenstackNode localNode = osNodeService.node(port.deviceId());
396
397 TrafficSelector selector = DefaultTrafficSelector.builder()
398 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
399 .matchArpOp(ARP.OP_REQUEST)
400 .matchArpTpa(port.ipAddress().getIp4Address())
401 .build();
402
403 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
404 }
405
406 /**
407 * Installs flow rules to match ARP reply packets only for VxLAN.
408 *
409 * @param port instance port
410 * @param install installation flag
411 */
412 private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
413
414 OpenstackNode localNode = osNodeService.node(port.deviceId());
415
416 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
417 setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
418 }
419
420 /**
421 * Installs flow rules to match ARP reply packets only for VLAN.
422 *
423 * @param port instance port
424 * @param install installation flag
425 */
426 private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
427
428 TrafficSelector selector = setArpReplyRuleForVnet(port, install);
429 setRemoteArpTreatmentForVlan(selector, port, install);
430 }
431
432 // a helper method
433 private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
434 TrafficSelector selector = DefaultTrafficSelector.builder()
435 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
436 .matchArpOp(ARP.OP_REPLY)
437 .matchArpTpa(port.ipAddress().getIp4Address())
438 .matchArpTha(port.macAddress())
439 .build();
440
441 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
442 .setOutput(port.portNumber())
443 .build();
444
445 osFlowRuleService.setRule(
446 appId,
447 port.deviceId(),
448 selector,
449 treatment,
450 PRIORITY_ARP_REPLY_RULE,
451 DHCP_ARP_TABLE,
452 install
453 );
454
455 return selector;
456 }
457
458 // a helper method
459 private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
460 InstancePort port,
461 OpenstackNode localNode,
462 boolean install) {
463 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
464 if (!remoteNode.intgBridge().equals(port.deviceId())) {
465 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
466 .extension(buildExtension(
467 deviceService,
468 remoteNode.intgBridge(),
469 localNode.dataIp().getIp4Address()),
470 remoteNode.intgBridge())
471 .setOutput(remoteNode.tunnelPortNum())
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 // a helper method
488 private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
489 InstancePort port,
490 boolean install) {
491 for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
492 if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
493 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
494 .setOutput(remoteNode.vlanPortNum())
495 .build();
496
497 osFlowRuleService.setRule(
498 appId,
499 remoteNode.intgBridge(),
500 selector,
501 treatmentToRemote,
502 PRIORITY_ARP_REQUEST_RULE,
503 DHCP_ARP_TABLE,
504 install);
505 }
506 }
507 }
508
509 /**
510 * Extracts properties from the component configuration context.
511 *
512 * @param context the component context
513 */
514 private void readComponentConfiguration(ComponentContext context) {
515 Dictionary<?, ?> properties = context.getProperties();
516
517 String updatedMac = Tools.get(properties, GATEWAY_MAC);
518 gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
519 log.info("Configured. Gateway MAC is {}", gatewayMac);
520 }
521
522 /**
Jian Lieae12362018-04-10 18:48:32 +0900523 * An internal packet processor which processes ARP request, and results in
524 * packet-out ARP reply.
525 */
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700526 private class InternalPacketProcessor implements PacketProcessor {
527
528 @Override
529 public void process(PacketContext context) {
530 if (context.isHandled()) {
531 return;
532 }
533
534 Ethernet ethPacket = context.inPacket().parsed();
535 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
536 return;
537 }
538 processPacketIn(context, ethPacket);
539 }
540 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900541
Jian Lieae12362018-04-10 18:48:32 +0900542 /**
543 * An internal network listener which listens to openstack network event,
544 * manages the gateway collection and installs flow rule that handles
545 * ARP request in data plane.
546 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900547 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
548
549 @Override
550 public boolean isRelevant(OpenstackNetworkEvent event) {
551 Subnet osSubnet = event.subnet();
552 if (osSubnet == null) {
553 return false;
554 }
Jian Lieae12362018-04-10 18:48:32 +0900555
Jian Libb4f5412018-04-12 09:48:50 +0900556 Network network = osNetworkService.network(osSubnet.getNetworkId());
557
Jian Lieae12362018-04-10 18:48:32 +0900558 if (network == null) {
559 log.warn("Network is not specified.");
560 return false;
561 } else {
562 if (network.getNetworkType().equals(NetworkType.FLAT)) {
563 return false;
564 }
565 }
566
567 // do not allow to proceed without leadership
568 NodeId leader = leadershipService.getLeader(appId.name());
569 if (!Objects.equals(localNodeId, leader)) {
570 return false;
571 }
572
Hyunsun Moon44aac662017-02-18 02:07:01 +0900573 return !Strings.isNullOrEmpty(osSubnet.getGateway());
574 }
575
576 @Override
577 public void event(OpenstackNetworkEvent event) {
578 switch (event.type()) {
579 case OPENSTACK_SUBNET_CREATED:
580 case OPENSTACK_SUBNET_UPDATED:
581 addSubnetGateway(event.subnet());
Daniel Park613ac372018-06-28 14:30:11 +0900582 setFakeGatewayArpRule(event.subnet(), true, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900583 break;
584 case OPENSTACK_SUBNET_REMOVED:
585 removeSubnetGateway(event.subnet());
Daniel Park613ac372018-06-28 14:30:11 +0900586 setFakeGatewayArpRule(event.subnet(), false, null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900587 break;
588 case OPENSTACK_NETWORK_CREATED:
589 case OPENSTACK_NETWORK_UPDATED:
590 case OPENSTACK_NETWORK_REMOVED:
591 case OPENSTACK_PORT_CREATED:
592 case OPENSTACK_PORT_UPDATED:
593 case OPENSTACK_PORT_REMOVED:
594 default:
595 // do nothing for the other events
596 break;
597 }
598 }
Jian Lieae12362018-04-10 18:48:32 +0900599 }
600
601 /**
602 * An internal openstack node listener which is used for listening openstack
603 * node activity. As long as a node is in complete state, we will install
604 * default ARP rule to handle ARP request.
605 */
606 private class InternalNodeEventListener implements OpenstackNodeListener {
607
608 @Override
609 public boolean isRelevant(OpenstackNodeEvent event) {
610 // do not allow to proceed without leadership
611 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900612 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
Jian Lieae12362018-04-10 18:48:32 +0900613 }
614
615 @Override
616 public void event(OpenstackNodeEvent event) {
617 OpenstackNode osNode = event.subject();
618 switch (event.type()) {
619 case OPENSTACK_NODE_COMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900620 setDefaultArpRule(osNode, true);
Jian Li5b66ce02018-07-09 22:43:54 +0900621 setAllArpRules(osNode, true);
622 addAllSubnetGateways();
Jian Lieae12362018-04-10 18:48:32 +0900623 break;
624 case OPENSTACK_NODE_INCOMPLETE:
Jian Li51b844c2018-05-31 10:59:03 +0900625 setDefaultArpRule(osNode, false);
Jian Li5b66ce02018-07-09 22:43:54 +0900626 setAllArpRules(osNode, false);
Jian Lieae12362018-04-10 18:48:32 +0900627 break;
Jian Lieae12362018-04-10 18:48:32 +0900628 default:
629 break;
630 }
631 }
632
Jian Li5b66ce02018-07-09 22:43:54 +0900633 private void addAllSubnetGateways() {
634 osNetworkService.networks().forEach(n -> {
635 if (n.getNetworkType() != NetworkType.FLAT) {
636 osNetworkService.subnets()
637 .stream()
638 .filter(s -> s.getNetworkId().equals(n.getId()))
639 .filter(s -> !Strings.isNullOrEmpty(s.getGateway()))
640 .filter(s -> !gateways.contains(s.getGateway()))
641 .forEach(OpenstackSwitchingArpHandler.this::addSubnetGateway);
642 }
643 });
644 }
645
Jian Lif96685c2018-05-21 14:14:16 +0900646 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900647 switch (getArpMode()) {
Jian Lif96685c2018-05-21 14:14:16 +0900648 case ARP_PROXY_MODE:
649 setDefaultArpRuleForProxyMode(osNode, install);
650 break;
651 case ARP_BROADCAST_MODE:
652 setDefaultArpRuleForBroadcastMode(osNode, install);
Jian Lie2e03a52018-07-05 23:35:02 +0900653
654 // we do not add fake gateway ARP rules for FLAT network
655 // ARP packets generated by FLAT typed VM should not be
656 // delegated to switch to handle
657 osNetworkService.subnets().stream().filter(subnet ->
658 osNetworkService.network(subnet.getNetworkId()) != null &&
659 osNetworkService.network(subnet.getNetworkId())
Jian Li7f70bb72018-07-06 23:35:30 +0900660 .getNetworkType() != NetworkType.FLAT)
Jian Lie2e03a52018-07-05 23:35:02 +0900661 .forEach(subnet ->
662 setFakeGatewayArpRule(subnet, install, osNode));
Jian Lif96685c2018-05-21 14:14:16 +0900663 break;
664 default:
665 log.warn("Invalid ARP mode {}. Please use either " +
Jian Li7f70bb72018-07-06 23:35:30 +0900666 "broadcast or proxy mode.", getArpMode());
Jian Lif96685c2018-05-21 14:14:16 +0900667 break;
Jian Lieae12362018-04-10 18:48:32 +0900668 }
669 }
Jian Lif96685c2018-05-21 14:14:16 +0900670
671 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
672 TrafficSelector selector = DefaultTrafficSelector.builder()
673 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
674 .build();
675
676 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
677 .punt()
678 .build();
679
680 osFlowRuleService.setRule(
681 appId,
682 osNode.intgBridge(),
683 selector,
684 treatment,
685 PRIORITY_ARP_CONTROL_RULE,
686 DHCP_ARP_TABLE,
687 install
688 );
689 }
690
691 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
692 TrafficSelector selector = DefaultTrafficSelector.builder()
693 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
694 .matchArpOp(ARP.OP_REQUEST)
695 .build();
696
697 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
698 .setOutput(PortNumber.FLOOD)
699 .build();
700
701 osFlowRuleService.setRule(
702 appId,
703 osNode.intgBridge(),
704 selector,
705 treatment,
706 PRIORITY_ARP_SUBNET_RULE,
707 DHCP_ARP_TABLE,
708 install
709 );
710 }
Jian Li7f70bb72018-07-06 23:35:30 +0900711
Jian Li5b66ce02018-07-09 22:43:54 +0900712 private void setAllArpRules(OpenstackNode osNode, boolean install) {
Jian Li7f70bb72018-07-06 23:35:30 +0900713 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
Jian Li5b66ce02018-07-09 22:43:54 +0900714 instancePortService.instancePorts().stream()
715 .filter(p -> p.deviceId().equals(osNode.intgBridge()))
716 .forEach(p -> {
717 setArpRequestRule(p, install);
718 setArpReplyRule(p, install);
Jian Li7f70bb72018-07-06 23:35:30 +0900719 });
720 }
721 }
Jian Lieae12362018-04-10 18:48:32 +0900722 }
723
724 /**
725 * An internal instance port listener which listens the port events generated
726 * from VM. When ARP a host which located in a remote compute node, we specify
727 * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
728 * host IP address. When ARP a host which located in a local compute node,
729 * we specify only ARP OP mode as REQUEST.
730 */
731 private class InternalInstancePortListener implements InstancePortListener {
732
733 @Override
734 public boolean isRelevant(InstancePortEvent event) {
735
Jian Li7f70bb72018-07-06 23:35:30 +0900736 if (ARP_PROXY_MODE.equals(getArpMode())) {
Jian Lieae12362018-04-10 18:48:32 +0900737 return false;
738 }
739
740 InstancePort instPort = event.subject();
741 return mastershipService.isLocalMaster(instPort.deviceId());
742 }
743
744 @Override
745 public void event(InstancePortEvent event) {
746 switch (event.type()) {
747 case OPENSTACK_INSTANCE_PORT_UPDATED:
748 case OPENSTACK_INSTANCE_PORT_DETECTED:
749 setArpRequestRule(event.subject(), true);
750 setArpReplyRule(event.subject(), true);
751 break;
752 case OPENSTACK_INSTANCE_PORT_VANISHED:
753 setArpRequestRule(event.subject(), false);
754 setArpReplyRule(event.subject(), false);
755 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900756 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
757 setArpRequestRule(event.subject(), false);
758 break;
Jian Lieae12362018-04-10 18:48:32 +0900759 default:
760 break;
761 }
762 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900763 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700764}