blob: 9e8d313ecb3d9ad87ead384f02b82bf583c37c2d [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 Moonb5f92e52016-02-17 15:02:06 -080018import com.google.common.collect.Maps;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070019import 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.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 Moon5401aaa2016-06-12 17:40:34 -070031import org.onosproject.net.packet.PacketProcessor;
32import org.onosproject.xosclient.api.VtnService;
33import org.opencord.cordvtn.api.CordVtnConfig;
alshabibb4d31712016-06-01 18:51:03 -070034import org.opencord.cordvtn.api.Instance;
Hyunsun Moon3fc17f72016-01-24 21:47:06 -080035import org.onosproject.net.Host;
Hyunsun Moon022272f2016-01-11 15:30:42 -080036import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.packet.DefaultOutboundPacket;
41import org.onosproject.net.packet.PacketContext;
42import org.onosproject.net.packet.PacketPriority;
43import org.onosproject.net.packet.PacketService;
44import org.slf4j.Logger;
45
46import java.nio.ByteBuffer;
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080047import java.util.Map;
Hyunsun Moon022272f2016-01-11 15:30:42 -080048import java.util.Optional;
49import java.util.Set;
50
51import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070052import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.PRIVATE;
Hyunsun Moon022272f2016-01-11 15:30:42 -080053import static org.slf4j.LoggerFactory.getLogger;
54
55/**
56 * Handles ARP requests for virtual network service IPs.
57 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070058@Component(immediate = true)
59public class CordVtnArpProxy extends AbstractInstanceHandler {
Hyunsun Moon022272f2016-01-11 15:30:42 -080060 protected final Logger log = getLogger(getClass());
Hyunsun Moon022272f2016-01-11 15:30:42 -080061
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 protected PacketService packetService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080064
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070065 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080066 private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
Hyunsun Moon022272f2016-01-11 15:30:42 -080067
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070068 private MacAddress privateGatewayMac = MacAddress.NONE;
69
70 @Activate
71 protected void activate() {
72 super.activate();
73 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
74 requestPacket();
75 }
76
77 @Deactivate
78 protected void deactivate() {
79 packetService.removeProcessor(packetProcessor);
80 super.deactivate();
Hyunsun Moon022272f2016-01-11 15:30:42 -080081 }
82
83 /**
84 * Requests ARP packet.
85 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070086 private void requestPacket() {
Hyunsun Moon022272f2016-01-11 15:30:42 -080087 TrafficSelector selector = DefaultTrafficSelector.builder()
88 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
89 .build();
90
91 packetService.requestPackets(selector,
92 PacketPriority.CONTROL,
93 appId,
94 Optional.empty());
95 }
96
97 /**
98 * Cancels ARP packet.
99 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700100 private void cancelPacket() {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800101 TrafficSelector selector = DefaultTrafficSelector.builder()
102 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
103 .build();
104
105 packetService.cancelPackets(selector,
106 PacketPriority.CONTROL,
107 appId,
108 Optional.empty());
109 }
110
111 /**
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800112 * Adds a given gateway IP and MAC address to this ARP proxy.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800113 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800114 * @param gatewayIp gateway ip address
115 * @param gatewayMac gateway mac address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800116 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700117 private void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800118 checkNotNull(gatewayIp);
119 checkNotNull(gatewayMac);
120 gateways.put(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon022272f2016-01-11 15:30:42 -0800121 }
122
123 /**
124 * Removes a given service IP address from this ARP proxy.
125 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800126 * @param gatewayIp gateway ip address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800127 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700128 private void removeGateway(IpAddress gatewayIp) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800129 checkNotNull(gatewayIp);
130 gateways.remove(gatewayIp.getIp4Address());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800131 }
132
133 /**
134 * Emits ARP reply with fake MAC address for a given ARP request.
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700135 * It only handles requests for the registered service IPs, and the other
136 * requests can be handled by other ARP handlers like openstackSwitching or
137 * proxyArp, for example.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800138 *
139 * @param context packet context
140 * @param ethPacket ethernet packet
141 */
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700142 private void processArpPacket(PacketContext context, Ethernet ethPacket) {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800143 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700144 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
145 return;
146 }
147
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800148 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800149
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800150 MacAddress gatewayMac = gateways.get(targetIp);
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700151 MacAddress replyMac = gatewayMac != null ? gatewayMac : getMacFromHostService(targetIp);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800152
153 if (replyMac.equals(MacAddress.NONE)) {
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700154 log.debug("Failed to find MAC for {}", targetIp.toString());
155 context.block();
Hyunsun Moon0d836e22016-02-01 23:30:58 -0800156 return;
157 }
158
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700159 log.trace("Send ARP reply for {} with {}", targetIp.toString(), replyMac.toString());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800160 Ethernet ethReply = ARP.buildArpReply(
161 targetIp,
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800162 replyMac,
Hyunsun Moon022272f2016-01-11 15:30:42 -0800163 ethPacket);
164
165 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
166 .setOutput(context.inPacket().receivedFrom().port())
167 .build();
168
169 packetService.emit(new DefaultOutboundPacket(
170 context.inPacket().receivedFrom().deviceId(),
171 treatment,
172 ByteBuffer.wrap(ethReply.serialize())));
173
174 context.block();
175 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800176
177 /**
178 * Emits gratuitous ARP when a gateway mac address has been changed.
179 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800180 * @param gatewayIp gateway ip address to update MAC
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700181 * @param instances set of instances to send gratuitous ARP packet
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800182 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700183 private void sendGratuitousArp(IpAddress gatewayIp, Set<Instance> instances) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800184 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
185 if (gatewayMac == null) {
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700186 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800187 return;
188 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800189
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800190 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700191 instances.stream().forEach(instance -> {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800192 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700193 .setOutput(instance.portNumber())
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800194 .build();
195
196 packetService.emit(new DefaultOutboundPacket(
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700197 instance.deviceId(),
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800198 treatment,
199 ByteBuffer.wrap(ethArp.serialize())));
200 });
201 }
202
203 /**
204 * Builds gratuitous ARP packet with a given IP and MAC address.
205 *
206 * @param ip ip address for TPA and SPA
207 * @param mac new mac address
208 * @return ethernet packet
209 */
210 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
211 Ethernet eth = new Ethernet();
212
213 eth.setEtherType(Ethernet.TYPE_ARP);
214 eth.setSourceMACAddress(mac);
215 eth.setDestinationMACAddress(MacAddress.BROADCAST);
216
217 ARP arp = new ARP();
218 arp.setOpCode(ARP.OP_REQUEST);
219 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
220 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
221 arp.setProtocolType(ARP.PROTO_TYPE_IP);
222 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
223
224 arp.setSenderHardwareAddress(mac.toBytes());
225 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
226 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
227 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
228
229 eth.setPayload(arp);
230 return eth;
231 }
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800232
233 /**
234 * Returns MAC address of a host with a given target IP address by asking to
235 * host service. It does not support overlapping IP.
236 *
237 * @param targetIp target ip
238 * @return mac address, or NONE mac address if it fails to find the mac
239 */
240 private MacAddress getMacFromHostService(IpAddress targetIp) {
241 checkNotNull(targetIp);
242
243 Host host = hostService.getHostsByIp(targetIp)
244 .stream()
245 .findFirst()
246 .orElse(null);
247
248 if (host != null) {
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700249 log.trace("Found MAC from host service for {}", targetIp.toString());
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800250 return host.mac();
251 } else {
252 return MacAddress.NONE;
253 }
254 }
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700255
256 private class InternalPacketProcessor implements PacketProcessor {
257
258 @Override
259 public void process(PacketContext context) {
260 if (context.isHandled()) {
261 return;
262 }
263 Ethernet ethPacket = context.inPacket().parsed();
264 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
265 return;
266 }
Hyunsun Moon315b9a62016-06-23 14:48:04 -0700267 processArpPacket(context, ethPacket);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700268 }
269 }
270
271 @Override
272 public void instanceDetected(Instance instance) {
273 VtnService service = getVtnService(instance.serviceId());
274 if (service == null) {
275 return;
276 }
277
278 if (service.networkType().equals(PRIVATE)) {
279 log.trace("Added IP:{} MAC:{}", service.serviceIp(), privateGatewayMac);
280 addGateway(service.serviceIp(), privateGatewayMac);
281 // send gratuitous ARP for the existing VMs when ONOS is restarted
282 sendGratuitousArp(service.serviceIp(), Sets.newHashSet(instance));
283 }
284 }
285
286 @Override
287 public void instanceRemoved(Instance instance) {
288 VtnService service = getVtnService(instance.serviceId());
289 if (service == null) {
290 return;
291 }
292
293 // remove this network gateway from proxy ARP if no instance presents
294 if (service.networkType().equals(PRIVATE) &&
295 getInstances(service.id()).isEmpty()) {
296 removeGateway(service.serviceIp());
297 }
298 }
299
300 @Override
301 protected void readConfiguration() {
302 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
303 if (config == null) {
304 log.debug("No configuration found");
305 return;
306 }
307
308 privateGatewayMac = config.privateGatewayMac();
309 log.debug("Added private gateway MAC {}", privateGatewayMac);
310
311 config.publicGateways().entrySet().stream().forEach(entry -> {
312 addGateway(entry.getKey(), entry.getValue());
313 log.debug("Added public gateway IP {}, MAC {}",
314 entry.getKey(), entry.getValue());
315 });
316 // TODO send gratuitous arp in case the MAC is changed
317
318 super.readConfiguration();
319 }
Hyunsun Moon022272f2016-01-11 15:30:42 -0800320}