blob: e507de5d895f446b4d03cba9489a66ae108527e7 [file] [log] [blame]
Hyunsun Moon022272f2016-01-11 15:30:42 -08001/*
Brian O'Connor80dff972017-08-03 22:46:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon022272f2016-01-11 15:30:42 -08003 *
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 */
alshabibb4d31712016-06-01 18:51:03 -070016package org.opencord.cordvtn.impl;
Hyunsun Moon022272f2016-01-11 15:30:42 -080017
Hyunsun Moonbcf49252017-02-21 22:28:41 +090018import com.google.common.base.Strings;
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080019import com.google.common.collect.Maps;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Hyunsun Moonbcf49252017-02-21 22:28:41 +090023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Hyunsun Moon022272f2016-01-11 15:30:42 -080027import org.onlab.packet.ARP;
28import org.onlab.packet.EthType;
29import org.onlab.packet.Ethernet;
30import org.onlab.packet.Ip4Address;
31import org.onlab.packet.IpAddress;
32import org.onlab.packet.MacAddress;
Hyunsun Moonbcf49252017-02-21 22:28:41 +090033import org.onlab.util.Tools;
34import org.onosproject.cfg.ComponentConfigService;
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090035import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Hyunsun Moonc031d9b2016-08-04 13:57:22 -070037import org.onosproject.net.DeviceId;
Hyunsun Moon187bf532017-01-19 10:57:40 +090038import org.onosproject.net.Host;
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +090039import org.onosproject.net.HostId;
40import org.onosproject.net.Port;
Hyunsun Moonc031d9b2016-08-04 13:57:22 -070041import org.onosproject.net.PortNumber;
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070042import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigListener;
Hyunsun Moon187bf532017-01-19 10:57:40 +090044import org.onosproject.net.config.NetworkConfigService;
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +090045import org.onosproject.net.device.DeviceService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080046import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090050import org.onosproject.net.host.HostService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080051import org.onosproject.net.packet.DefaultOutboundPacket;
52import org.onosproject.net.packet.PacketContext;
53import org.onosproject.net.packet.PacketPriority;
Hyunsun Moon187bf532017-01-19 10:57:40 +090054import org.onosproject.net.packet.PacketProcessor;
Hyunsun Moon022272f2016-01-11 15:30:42 -080055import org.onosproject.net.packet.PacketService;
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090056import org.opencord.cordvtn.api.Constants;
Hyunsun Moon187bf532017-01-19 10:57:40 +090057import org.opencord.cordvtn.api.CordVtnConfig;
58import org.opencord.cordvtn.api.core.Instance;
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090059import org.opencord.cordvtn.api.core.ServiceNetworkEvent;
60import org.opencord.cordvtn.api.core.ServiceNetworkListener;
61import org.opencord.cordvtn.api.core.ServiceNetworkService;
Hyunsun Moon187bf532017-01-19 10:57:40 +090062import org.opencord.cordvtn.api.net.ServiceNetwork;
Hyunsun Moon0d00fb42017-08-09 18:21:03 +090063import org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType;
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +090064import org.opencord.cordvtn.api.node.CordVtnNode;
65import org.opencord.cordvtn.api.node.CordVtnNodeService;
Hyunsun Moon0d00fb42017-08-09 18:21:03 +090066import org.opencord.cordvtn.api.node.CordVtnNodeState;
Hyunsun Moonbcf49252017-02-21 22:28:41 +090067import org.osgi.service.component.ComponentContext;
Hyunsun Moon022272f2016-01-11 15:30:42 -080068import org.slf4j.Logger;
69
70import java.nio.ByteBuffer;
Hyunsun Moonbcf49252017-02-21 22:28:41 +090071import java.util.Dictionary;
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080072import java.util.Map;
Hyunsun Moon022272f2016-01-11 15:30:42 -080073import java.util.Optional;
74import java.util.Set;
75
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090076import static com.google.common.base.Preconditions.checkArgument;
Hyunsun Moon022272f2016-01-11 15:30:42 -080077import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +090078import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Hyunsun Moonbcf49252017-02-21 22:28:41 +090079import static org.opencord.cordvtn.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Hyunsun Moon187bf532017-01-19 10:57:40 +090080import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.*;
Hyunsun Moon022272f2016-01-11 15:30:42 -080081import static org.slf4j.LoggerFactory.getLogger;
82
83/**
84 * Handles ARP requests for virtual network service IPs.
85 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070086@Component(immediate = true)
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090087public class CordVtnArpProxy {
Hyunsun Moon022272f2016-01-11 15:30:42 -080088 protected final Logger log = getLogger(getClass());
Hyunsun Moon022272f2016-01-11 15:30:42 -080089
Hyunsun Moonbcf49252017-02-21 22:28:41 +090090 private static final String PRIVATE_GATEWAY_MAC = "privateGatewayMac";
91
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected PacketService packetService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080094
Hyunsun Moonc031d9b2016-08-04 13:57:22 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon4302c2b2017-01-19 14:20:34 +090096 protected CoreService coreService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected HostService hostService;
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700100
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900102 protected NetworkConfigService netConfigService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected ComponentConfigService compConfigService;
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700106
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +0900108 protected DeviceService deviceService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected CordVtnNodeService nodeService;
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected ServiceNetworkService snetService;
115
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900116 @Property(name = PRIVATE_GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
117 label = "Fake MAC address for virtual network gateway")
118 private String privateGatewayMacStr = DEFAULT_GATEWAY_MAC_STR;
119 private MacAddress privateGatewayMac = MacAddress.valueOf(privateGatewayMacStr);
120
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700121 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon8e9bc932017-01-31 21:32:20 +0900122 private final Map<IpAddress, MacAddress> gateways = Maps.newConcurrentMap();
Hyunsun Moon022272f2016-01-11 15:30:42 -0800123
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700124 private NetworkConfigListener configListener = new InternalConfigListener();
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900125 private ServiceNetworkListener snetListener = new InternalServiceNetworkListener();
126 private ApplicationId appId;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700127
128 @Activate
129 protected void activate() {
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900130 appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900131 compConfigService.registerProperties(getClass());
132
133 netConfigService.addListener(configListener);
134 readPublicGateways();
135 snetService.addListener(snetListener);
136 readPrivateGateways();
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700137
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700138 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
139 requestPacket();
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900140
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900141 log.info("Started");
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700142 }
143
144 @Deactivate
145 protected void deactivate() {
146 packetService.removeProcessor(packetProcessor);
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900147 snetService.removeListener(snetListener);
148 netConfigService.removeListener(configListener);
149 compConfigService.unregisterProperties(getClass(), false);
150
151 log.info("Stopped");
152 }
153
154 @Modified
155 protected void modified(ComponentContext context) {
156 Dictionary<?, ?> properties = context.getProperties();
157 String updatedMac;
158
159 updatedMac = Tools.get(properties, PRIVATE_GATEWAY_MAC);
160 if (!Strings.isNullOrEmpty(updatedMac) &&
161 !updatedMac.equals(privateGatewayMacStr)) {
162 privateGatewayMacStr = updatedMac;
163 privateGatewayMac = MacAddress.valueOf(privateGatewayMacStr);
164 }
165
166 log.info("Modified");
Hyunsun Moon022272f2016-01-11 15:30:42 -0800167 }
168
169 /**
170 * Requests ARP packet.
171 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700172 private void requestPacket() {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800173 TrafficSelector selector = DefaultTrafficSelector.builder()
174 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
175 .build();
176
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900177 packetService.requestPackets(
178 selector,
179 PacketPriority.CONTROL,
180 appId,
181 Optional.empty());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800182 }
183
184 /**
185 * Cancels ARP packet.
186 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700187 private void cancelPacket() {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800188 TrafficSelector selector = DefaultTrafficSelector.builder()
189 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
190 .build();
191
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900192 packetService.cancelPackets(
193 selector,
194 PacketPriority.CONTROL,
195 appId,
196 Optional.empty());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800197 }
198
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900199 private void readPrivateGateways() {
200 snetService.serviceNetworks().stream()
201 .filter(net -> net.type() == PRIVATE || net.type() == VSG)
202 .filter(net -> net.serviceIp() != null)
203 .forEach(net -> addGateway(net.serviceIp(), privateGatewayMac));
204 }
205
Hyunsun Moon022272f2016-01-11 15:30:42 -0800206 /**
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800207 * Adds a given gateway IP and MAC address to this ARP proxy.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800208 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800209 * @param gatewayIp gateway ip address
210 * @param gatewayMac gateway mac address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800211 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700212 private void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900213 checkNotNull(gatewayIp, "Gateway IP address cannot be null");
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900214 checkArgument(gatewayMac != null && gatewayMac != MacAddress.NONE,
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900215 "Gateway MAC address cannot be null or NONE");
Hyunsun Moon8e9bc932017-01-31 21:32:20 +0900216
217 MacAddress existing = gateways.get(gatewayIp);
218 if (existing != null && !existing.equals(privateGatewayMac) &&
219 gatewayMac.equals(privateGatewayMac)) {
220 // this is public gateway IP and MAC configured via netcfg
221 // don't update with private gateway MAC
222 return;
223 }
224 gateways.put(gatewayIp, gatewayMac);
225 log.debug("Added ARP proxy entry IP:{} MAC:{}", gatewayIp, gatewayMac);
Hyunsun Moon022272f2016-01-11 15:30:42 -0800226 }
227
228 /**
229 * Removes a given service IP address from this ARP proxy.
230 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800231 * @param gatewayIp gateway ip address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800232 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700233 private void removeGateway(IpAddress gatewayIp) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800234 checkNotNull(gatewayIp);
Hyunsun Moon8e9bc932017-01-31 21:32:20 +0900235 MacAddress existing = gateways.get(gatewayIp);
236 if (existing == null) {
237 return;
238 }
239 if (!existing.equals(privateGatewayMac)) {
240 // this is public gateway IP and MAC configured via netcfg
241 // do nothing
242 return;
243 }
244 gateways.remove(gatewayIp);
245 log.debug("Removed ARP proxy entry for IP:{} MAC: {}", gatewayIp, existing);
Hyunsun Moon022272f2016-01-11 15:30:42 -0800246 }
247
248 /**
249 * Emits ARP reply with fake MAC address for a given ARP request.
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700250 * It only handles requests for the registered gateway IPs and host IPs.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800251 *
252 * @param context packet context
253 * @param ethPacket ethernet packet
254 */
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700255 private void processArpRequest(PacketContext context, Ethernet ethPacket) {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800256 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800257 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800258
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800259 MacAddress gatewayMac = gateways.get(targetIp);
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700260 MacAddress replyMac = gatewayMac != null ? gatewayMac :
261 getMacFromHostService(targetIp);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800262
263 if (replyMac.equals(MacAddress.NONE)) {
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900264 forwardArpRequest(context, ethPacket);
Hyunsun Moon0d836e22016-02-01 23:30:58 -0800265 return;
266 }
267
Hyunsun Moon022272f2016-01-11 15:30:42 -0800268 Ethernet ethReply = ARP.buildArpReply(
269 targetIp,
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800270 replyMac,
Hyunsun Moon022272f2016-01-11 15:30:42 -0800271 ethPacket);
272
273 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
274 .setOutput(context.inPacket().receivedFrom().port())
275 .build();
276
277 packetService.emit(new DefaultOutboundPacket(
278 context.inPacket().receivedFrom().deviceId(),
279 treatment,
280 ByteBuffer.wrap(ethReply.serialize())));
281
282 context.block();
283 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800284
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700285 private void processArpReply(PacketContext context, Ethernet ethPacket) {
286 ARP arpPacket = (ARP) ethPacket.getPayload();
287 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
288
289 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
290 Host host = hostService.getHostsByIp(targetIp).stream()
291 .filter(h -> h.location().deviceId().equals(deviceId))
292 .findFirst()
293 .orElse(null);
294
295 if (host == null) {
296 // do nothing for the unknown ARP reply
297 log.trace("No host found for {} in {}", targetIp, deviceId);
298 context.block();
299 return;
300 }
301
302 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
303 .setOutput(host.location().port())
304 .build();
305
306 packetService.emit(new DefaultOutboundPacket(
307 deviceId,
308 treatment,
309 ByteBuffer.wrap(ethPacket.serialize())));
310
311 context.block();
312 }
313
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900314 private void forwardArpRequest(PacketContext context, Ethernet ethPacket) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700315 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900316 PortNumber inputPort = context.inPacket().receivedFrom().port();
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700317
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900318 Host host = hostService.getHost(HostId.hostId(ethPacket.getSourceMAC()));
319 NetworkType networkType = Instance.of(host).netType();
320 if (host == null || (networkType != MANAGEMENT_HOST &&
321 networkType != FLAT)) {
322 context.block();
323 log.trace("Failed to handle ARP request");
324 return;
325 }
326
327 PortNumber outputPort = networkType == MANAGEMENT_HOST ?
328 hostMgmtPort(deviceId) : dataPort(deviceId);
329 if (inputPort.equals(outputPort)) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700330 context.block();
331 return;
332 }
333
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900334 if (outputPort != null) {
335 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
336 .setOutput(outputPort)
337 .build();
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700338
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900339 packetService.emit(new DefaultOutboundPacket(
340 context.inPacket().receivedFrom().deviceId(),
341 treatment,
342 ByteBuffer.wrap(ethPacket.serialize())));
343 } else {
344 log.trace("Failed to handle ARP request");
345 }
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700346
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700347 context.block();
348 }
349
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +0900350 private PortNumber hostMgmtPort(DeviceId deviceId) {
351 CordVtnNode node = nodeService.node(deviceId);
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900352 if (node == null || node.state() != CordVtnNodeState.COMPLETE ||
353 node.hostManagementInterface() == null) {
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +0900354 return null;
355 }
356 Optional<Port> port = deviceService.getPorts(deviceId).stream()
357 .filter(p -> p.annotations().value(PORT_NAME)
358 .equals(node.hostManagementInterface()) &&
359 p.isEnabled())
360 .findAny();
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900361 return port.map(Port::number).orElse(null);
362 }
363
364 private PortNumber dataPort(DeviceId deviceId) {
365 CordVtnNode node = nodeService.node(deviceId);
366 if (node == null || node.state() != CordVtnNodeState.COMPLETE ||
367 node.dataInterface() == null) {
368 return null;
369 }
370 Optional<Port> port = deviceService.getPorts(deviceId).stream()
371 .filter(p -> p.annotations().value(PORT_NAME)
372 .equals(node.dataInterface()) &&
373 p.isEnabled())
374 .findAny();
375 return port.map(Port::number).orElse(null);
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +0900376 }
377
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800378 /**
379 * Emits gratuitous ARP when a gateway mac address has been changed.
380 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800381 * @param gatewayIp gateway ip address to update MAC
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700382 * @param instances set of instances to send gratuitous ARP packet
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800383 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700384 private void sendGratuitousArp(IpAddress gatewayIp, Set<Instance> instances) {
Hyunsun Moon8e9bc932017-01-31 21:32:20 +0900385 MacAddress gatewayMac = gateways.get(gatewayIp);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800386 if (gatewayMac == null) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700387 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800388 return;
389 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800390
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800391 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900392 instances.forEach(instance -> {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800393 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700394 .setOutput(instance.portNumber())
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800395 .build();
396
397 packetService.emit(new DefaultOutboundPacket(
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700398 instance.deviceId(),
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800399 treatment,
400 ByteBuffer.wrap(ethArp.serialize())));
401 });
402 }
403
404 /**
405 * Builds gratuitous ARP packet with a given IP and MAC address.
406 *
407 * @param ip ip address for TPA and SPA
408 * @param mac new mac address
409 * @return ethernet packet
410 */
411 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
412 Ethernet eth = new Ethernet();
413
414 eth.setEtherType(Ethernet.TYPE_ARP);
415 eth.setSourceMACAddress(mac);
416 eth.setDestinationMACAddress(MacAddress.BROADCAST);
417
418 ARP arp = new ARP();
419 arp.setOpCode(ARP.OP_REQUEST);
420 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
421 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
422 arp.setProtocolType(ARP.PROTO_TYPE_IP);
423 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
424
425 arp.setSenderHardwareAddress(mac.toBytes());
426 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
427 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
428 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
429
430 eth.setPayload(arp);
431 return eth;
432 }
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800433
434 /**
435 * Returns MAC address of a host with a given target IP address by asking to
436 * host service. It does not support overlapping IP.
437 *
438 * @param targetIp target ip
439 * @return mac address, or NONE mac address if it fails to find the mac
440 */
441 private MacAddress getMacFromHostService(IpAddress targetIp) {
442 checkNotNull(targetIp);
443
444 Host host = hostService.getHostsByIp(targetIp)
445 .stream()
446 .findFirst()
447 .orElse(null);
448
449 if (host != null) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700450 log.trace("Found MAC from host service for {}", targetIp);
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800451 return host.mac();
452 } else {
453 return MacAddress.NONE;
454 }
455 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700456
457 private class InternalPacketProcessor implements PacketProcessor {
458
459 @Override
460 public void process(PacketContext context) {
461 if (context.isHandled()) {
462 return;
463 }
464 Ethernet ethPacket = context.inPacket().parsed();
465 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
466 return;
467 }
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700468
469 ARP arpPacket = (ARP) ethPacket.getPayload();
470 switch (arpPacket.getOpCode()) {
471 case ARP.OP_REQUEST:
472 processArpRequest(context, ethPacket);
473 break;
474 case ARP.OP_REPLY:
475 processArpReply(context, ethPacket);
476 break;
477 default:
478 break;
479 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700480 }
481 }
482
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900483 private class InternalServiceNetworkListener implements ServiceNetworkListener {
484
485 @Override
486 public boolean isRelevant(ServiceNetworkEvent event) {
487 ServiceNetwork snet = event.subject();
Hyunsun Moon8e9bc932017-01-31 21:32:20 +0900488 return snet.serviceIp() != null;
Hyunsun Moon1e88fef2016-08-04 14:00:35 -0700489 }
490
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900491 @Override
492 public void event(ServiceNetworkEvent event) {
493 ServiceNetwork snet = event.subject();
494 switch (event.type()) {
495 case SERVICE_NETWORK_CREATED:
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900496 if (snet.type() == PRIVATE || snet.type() == VSG) {
497 addGateway(snet.serviceIp(), privateGatewayMac);
498 }
499 break;
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900500 case SERVICE_NETWORK_UPDATED:
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900501 if (snet.type() == PRIVATE || snet.type() == VSG) {
502 addGateway(snet.serviceIp(), privateGatewayMac);
503 } else {
504 // don't service ARP for the other network gateway
505 removeGateway(snet.serviceIp());
506 }
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900507 break;
508 case SERVICE_NETWORK_REMOVED:
509 removeGateway(snet.serviceIp());
510 break;
511 case SERVICE_PORT_CREATED:
512 case SERVICE_PORT_UPDATED:
513 case SERVICE_PORT_REMOVED:
514 default:
515 // do nothing for the other events
516 break;
517 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700518 }
519 }
520
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900521 private void readPublicGateways() {
522 CordVtnConfig config = netConfigService.getConfig(appId, CordVtnConfig.class);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700523 if (config == null) {
Hyunsun Moon2c3f0ee2017-04-06 16:47:21 +0900524 log.warn("No configuration found");
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700525 return;
526 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700527
Hyunsun Moon0d00fb42017-08-09 18:21:03 +0900528 config.publicGateways().forEach(this::addGateway);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700529 // TODO send gratuitous arp in case the MAC is changed
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700530 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700531
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700532 private class InternalConfigListener implements NetworkConfigListener {
533
534 @Override
Hyunsun Moon4302c2b2017-01-19 14:20:34 +0900535 public boolean isRelevant(NetworkConfigEvent event) {
536 return event.configClass().equals(CordVtnConfig.class);
537 }
538
539 @Override
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700540 public void event(NetworkConfigEvent event) {
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700541
542 switch (event.type()) {
543 case CONFIG_ADDED:
544 case CONFIG_UPDATED:
Hyunsun Moonbcf49252017-02-21 22:28:41 +0900545 readPublicGateways();
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700546 break;
547 default:
548 break;
549 }
550 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700551 }
Hyunsun Moon022272f2016-01-11 15:30:42 -0800552}