blob: c2e2a6242188337b9c96bdcf1f6d3486972c7f22 [file] [log] [blame]
Hyunsun Moon022272f2016-01-11 15:30:42 -08001/*
Brian O'Connor8e57fd52016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
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 Mooneaf75e62016-09-27 16:40:23 -070018import com.google.common.collect.ImmutableSet;
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;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Hyunsun Moon022272f2016-01-11 15:30:42 -080025import org.onlab.packet.ARP;
26import org.onlab.packet.EthType;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.Ip4Address;
29import org.onlab.packet.IpAddress;
30import org.onlab.packet.MacAddress;
Hyunsun Moonc031d9b2016-08-04 13:57:22 -070031import org.onosproject.net.DeviceId;
32import org.onosproject.net.PortNumber;
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070033import org.onosproject.net.config.NetworkConfigEvent;
34import org.onosproject.net.config.NetworkConfigListener;
35import org.onosproject.net.config.NetworkConfigRegistry;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070036import org.onosproject.net.packet.PacketProcessor;
Hyunsun Moonfd5a24e2016-10-19 19:15:48 -070037import org.opencord.cordvtn.api.config.CordVtnConfig;
38import org.opencord.cordvtn.api.instance.Instance;
Hyunsun Moon3fc17f72016-01-24 21:47:06 -080039import org.onosproject.net.Host;
Hyunsun Moon022272f2016-01-11 15:30:42 -080040import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.packet.DefaultOutboundPacket;
45import org.onosproject.net.packet.PacketContext;
46import org.onosproject.net.packet.PacketPriority;
47import org.onosproject.net.packet.PacketService;
Hyunsun Moonfd5a24e2016-10-19 19:15:48 -070048import org.opencord.cordvtn.api.net.VtnNetwork;
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070049import org.opencord.cordvtn.impl.handler.AbstractInstanceHandler;
Hyunsun Moon022272f2016-01-11 15:30:42 -080050import org.slf4j.Logger;
51
52import java.nio.ByteBuffer;
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080053import java.util.Map;
Hyunsun Moon022272f2016-01-11 15:30:42 -080054import java.util.Optional;
55import java.util.Set;
56
57import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moonfd5a24e2016-10-19 19:15:48 -070058import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.*;
Hyunsun Moon022272f2016-01-11 15:30:42 -080059import static org.slf4j.LoggerFactory.getLogger;
60
61/**
62 * Handles ARP requests for virtual network service IPs.
63 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070064@Component(immediate = true)
65public class CordVtnArpProxy extends AbstractInstanceHandler {
Hyunsun Moon022272f2016-01-11 15:30:42 -080066 protected final Logger log = getLogger(getClass());
Hyunsun Moon022272f2016-01-11 15:30:42 -080067
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070068 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected PacketService packetService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080070
Hyunsun Moonc031d9b2016-08-04 13:57:22 -070071 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected CordVtnNodeManager nodeManager;
73
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070074 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected NetworkConfigRegistry configRegistry;
76
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070077 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080078 private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
Hyunsun Moon022272f2016-01-11 15:30:42 -080079
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070080 private MacAddress privateGatewayMac = MacAddress.NONE;
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070081 private NetworkConfigListener configListener = new InternalConfigListener();
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070082
83 @Activate
84 protected void activate() {
85 super.activate();
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070086 configRegistry.addListener(configListener);
87 readConfiguration();
88
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070089 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
90 requestPacket();
91 }
92
93 @Deactivate
94 protected void deactivate() {
95 packetService.removeProcessor(packetProcessor);
Hyunsun Mooneaf75e62016-09-27 16:40:23 -070096 configRegistry.removeListener(configListener);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070097 super.deactivate();
Hyunsun Moon022272f2016-01-11 15:30:42 -080098 }
99
100 /**
101 * Requests ARP packet.
102 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700103 private void requestPacket() {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800104 TrafficSelector selector = DefaultTrafficSelector.builder()
105 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
106 .build();
107
108 packetService.requestPackets(selector,
109 PacketPriority.CONTROL,
110 appId,
111 Optional.empty());
112 }
113
114 /**
115 * Cancels ARP packet.
116 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700117 private void cancelPacket() {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800118 TrafficSelector selector = DefaultTrafficSelector.builder()
119 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
120 .build();
121
122 packetService.cancelPackets(selector,
123 PacketPriority.CONTROL,
124 appId,
125 Optional.empty());
126 }
127
128 /**
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800129 * Adds a given gateway IP and MAC address to this ARP proxy.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800130 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800131 * @param gatewayIp gateway ip address
132 * @param gatewayMac gateway mac address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800133 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700134 private void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800135 checkNotNull(gatewayIp);
136 checkNotNull(gatewayMac);
137 gateways.put(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon022272f2016-01-11 15:30:42 -0800138 }
139
140 /**
141 * Removes a given service IP address from this ARP proxy.
142 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800143 * @param gatewayIp gateway ip address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800144 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700145 private void removeGateway(IpAddress gatewayIp) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800146 checkNotNull(gatewayIp);
147 gateways.remove(gatewayIp.getIp4Address());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800148 }
149
150 /**
151 * Emits ARP reply with fake MAC address for a given ARP request.
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700152 * It only handles requests for the registered gateway IPs and host IPs.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800153 *
154 * @param context packet context
155 * @param ethPacket ethernet packet
156 */
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700157 private void processArpRequest(PacketContext context, Ethernet ethPacket) {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800158 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800159 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800160
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800161 MacAddress gatewayMac = gateways.get(targetIp);
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700162 MacAddress replyMac = gatewayMac != null ? gatewayMac :
163 getMacFromHostService(targetIp);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800164
165 if (replyMac.equals(MacAddress.NONE)) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700166 log.trace("Failed to find MAC for {}", targetIp);
167 forwardManagementArpRequest(context, ethPacket);
Hyunsun Moon0d836e22016-02-01 23:30:58 -0800168 return;
169 }
170
Hyunsun Moon022272f2016-01-11 15:30:42 -0800171 Ethernet ethReply = ARP.buildArpReply(
172 targetIp,
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800173 replyMac,
Hyunsun Moon022272f2016-01-11 15:30:42 -0800174 ethPacket);
175
176 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
177 .setOutput(context.inPacket().receivedFrom().port())
178 .build();
179
180 packetService.emit(new DefaultOutboundPacket(
181 context.inPacket().receivedFrom().deviceId(),
182 treatment,
183 ByteBuffer.wrap(ethReply.serialize())));
184
185 context.block();
186 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800187
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700188 private void processArpReply(PacketContext context, Ethernet ethPacket) {
189 ARP arpPacket = (ARP) ethPacket.getPayload();
190 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
191
192 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
193 Host host = hostService.getHostsByIp(targetIp).stream()
194 .filter(h -> h.location().deviceId().equals(deviceId))
195 .findFirst()
196 .orElse(null);
197
198 if (host == null) {
199 // do nothing for the unknown ARP reply
200 log.trace("No host found for {} in {}", targetIp, deviceId);
201 context.block();
202 return;
203 }
204
205 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
206 .setOutput(host.location().port())
207 .build();
208
209 packetService.emit(new DefaultOutboundPacket(
210 deviceId,
211 treatment,
212 ByteBuffer.wrap(ethPacket.serialize())));
213
214 context.block();
215 }
216
217 private void forwardManagementArpRequest(PacketContext context, Ethernet ethPacket) {
218 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
219 PortNumber hostMgmtPort = nodeManager.hostManagementPort(deviceId);
220 Host host = hostService.getConnectedHosts(context.inPacket().receivedFrom())
221 .stream()
222 .findFirst().orElse(null);
223
224 if (host == null ||
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700225 !Instance.of(host).netType().name().contains("MANAGEMENT") ||
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700226 hostMgmtPort == null) {
227 context.block();
228 return;
229 }
230
231 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
232 .setOutput(hostMgmtPort)
233 .build();
234
235 packetService.emit(new DefaultOutboundPacket(
236 context.inPacket().receivedFrom().deviceId(),
237 treatment,
238 ByteBuffer.wrap(ethPacket.serialize())));
239
240 log.trace("Forward ARP request to management network");
241 context.block();
242 }
243
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800244 /**
245 * Emits gratuitous ARP when a gateway mac address has been changed.
246 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800247 * @param gatewayIp gateway ip address to update MAC
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700248 * @param instances set of instances to send gratuitous ARP packet
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800249 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700250 private void sendGratuitousArp(IpAddress gatewayIp, Set<Instance> instances) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800251 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
252 if (gatewayMac == null) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700253 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800254 return;
255 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800256
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800257 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700258 instances.stream().forEach(instance -> {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800259 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700260 .setOutput(instance.portNumber())
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800261 .build();
262
263 packetService.emit(new DefaultOutboundPacket(
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700264 instance.deviceId(),
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800265 treatment,
266 ByteBuffer.wrap(ethArp.serialize())));
267 });
268 }
269
270 /**
271 * Builds gratuitous ARP packet with a given IP and MAC address.
272 *
273 * @param ip ip address for TPA and SPA
274 * @param mac new mac address
275 * @return ethernet packet
276 */
277 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
278 Ethernet eth = new Ethernet();
279
280 eth.setEtherType(Ethernet.TYPE_ARP);
281 eth.setSourceMACAddress(mac);
282 eth.setDestinationMACAddress(MacAddress.BROADCAST);
283
284 ARP arp = new ARP();
285 arp.setOpCode(ARP.OP_REQUEST);
286 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
287 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
288 arp.setProtocolType(ARP.PROTO_TYPE_IP);
289 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
290
291 arp.setSenderHardwareAddress(mac.toBytes());
292 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
293 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
294 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
295
296 eth.setPayload(arp);
297 return eth;
298 }
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800299
300 /**
301 * Returns MAC address of a host with a given target IP address by asking to
302 * host service. It does not support overlapping IP.
303 *
304 * @param targetIp target ip
305 * @return mac address, or NONE mac address if it fails to find the mac
306 */
307 private MacAddress getMacFromHostService(IpAddress targetIp) {
308 checkNotNull(targetIp);
309
310 Host host = hostService.getHostsByIp(targetIp)
311 .stream()
312 .findFirst()
313 .orElse(null);
314
315 if (host != null) {
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700316 log.trace("Found MAC from host service for {}", targetIp);
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800317 return host.mac();
318 } else {
319 return MacAddress.NONE;
320 }
321 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700322
323 private class InternalPacketProcessor implements PacketProcessor {
324
325 @Override
326 public void process(PacketContext context) {
327 if (context.isHandled()) {
328 return;
329 }
330 Ethernet ethPacket = context.inPacket().parsed();
331 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
332 return;
333 }
Hyunsun Moonc031d9b2016-08-04 13:57:22 -0700334
335 ARP arpPacket = (ARP) ethPacket.getPayload();
336 switch (arpPacket.getOpCode()) {
337 case ARP.OP_REQUEST:
338 processArpRequest(context, ethPacket);
339 break;
340 case ARP.OP_REPLY:
341 processArpReply(context, ethPacket);
342 break;
343 default:
344 break;
345 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700346 }
347 }
348
349 @Override
350 public void instanceDetected(Instance instance) {
Hyunsun Moon1e88fef2016-08-04 14:00:35 -0700351 // TODO remove this when XOS provides access agent information
352 // and handle it the same way wit the other instances
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700353 if (instance.netType() == ACCESS_AGENT) {
Hyunsun Moon1e88fef2016-08-04 14:00:35 -0700354 return;
355 }
356
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700357 VtnNetwork vtnNet = getVtnNetwork(instance);
358 if (vtnNet.type() != PUBLIC && vtnNet.type() != MANAGEMENT_HOST &&
359 vtnNet.type() != MANAGEMENT_LOCAL) {
360 log.trace("Added IP:{} MAC:{}", vtnNet.serviceIp(), privateGatewayMac);
361 addGateway(vtnNet.serviceIp(), privateGatewayMac);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700362 // send gratuitous ARP for the existing VMs when ONOS is restarted
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700363 sendGratuitousArp(vtnNet.serviceIp(), ImmutableSet.of(instance));
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700364 }
365 }
366
367 @Override
368 public void instanceRemoved(Instance instance) {
Hyunsun Moon1e88fef2016-08-04 14:00:35 -0700369 // TODO remove this when XOS provides access agent information
370 // and handle it the same way wit the other instances
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700371 if (instance.netType() == ACCESS_AGENT) {
Hyunsun Moon1e88fef2016-08-04 14:00:35 -0700372 return;
373 }
374
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700375 VtnNetwork vtnNet = getVtnNetwork(instance);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700376 // remove this network gateway from proxy ARP if no instance presents
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700377 if (vtnNet.type() == PRIVATE &&
378 getInstances(vtnNet.id()).isEmpty()) {
379 removeGateway(vtnNet.serviceIp());
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700380 }
381 }
382
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700383 private void readConfiguration() {
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700384 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
385 if (config == null) {
386 log.debug("No configuration found");
387 return;
388 }
389
390 privateGatewayMac = config.privateGatewayMac();
391 log.debug("Added private gateway MAC {}", privateGatewayMac);
392
393 config.publicGateways().entrySet().stream().forEach(entry -> {
394 addGateway(entry.getKey(), entry.getValue());
395 log.debug("Added public gateway IP {}, MAC {}",
396 entry.getKey(), entry.getValue());
397 });
398 // TODO send gratuitous arp in case the MAC is changed
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700399 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700400
Hyunsun Mooneaf75e62016-09-27 16:40:23 -0700401 private class InternalConfigListener implements NetworkConfigListener {
402
403 @Override
404 public void event(NetworkConfigEvent event) {
405 if (!event.configClass().equals(CordVtnConfig.class)) {
406 return;
407 }
408
409 switch (event.type()) {
410 case CONFIG_ADDED:
411 case CONFIG_UPDATED:
412 readConfiguration();
413 break;
414 default:
415 break;
416 }
417 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700418 }
Hyunsun Moon022272f2016-01-11 15:30:42 -0800419}