blob: 6d29a7d48a8f80810e618434481e9e932b2eb3dd [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 Moon022272f2016-01-11 15:30:42 -080019import org.onlab.packet.ARP;
20import org.onlab.packet.EthType;
21import org.onlab.packet.Ethernet;
22import org.onlab.packet.Ip4Address;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.MacAddress;
alshabibb4d31712016-06-01 18:51:03 -070025import org.opencord.cordvtn.api.Instance;
Hyunsun Moon022272f2016-01-11 15:30:42 -080026import org.onosproject.core.ApplicationId;
Hyunsun Moon3fc17f72016-01-24 21:47:06 -080027import org.onosproject.net.Host;
Hyunsun Moon022272f2016-01-11 15:30:42 -080028import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
30import org.onosproject.net.flow.TrafficSelector;
31import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moonb6febbe2016-02-12 15:59:53 -080032import org.onosproject.net.host.HostService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080033import org.onosproject.net.packet.DefaultOutboundPacket;
34import org.onosproject.net.packet.PacketContext;
35import org.onosproject.net.packet.PacketPriority;
36import org.onosproject.net.packet.PacketService;
37import org.slf4j.Logger;
38
39import java.nio.ByteBuffer;
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080040import java.util.Map;
Hyunsun Moon022272f2016-01-11 15:30:42 -080041import java.util.Optional;
42import java.util.Set;
43
44import static com.google.common.base.Preconditions.checkNotNull;
45import static org.slf4j.LoggerFactory.getLogger;
46
47/**
48 * Handles ARP requests for virtual network service IPs.
49 */
50public class CordVtnArpProxy {
51 protected final Logger log = getLogger(getClass());
Hyunsun Moon022272f2016-01-11 15:30:42 -080052
53 private final ApplicationId appId;
54 private final PacketService packetService;
Hyunsun Moonb6febbe2016-02-12 15:59:53 -080055 private final HostService hostService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080056
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080057 private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
Hyunsun Moon022272f2016-01-11 15:30:42 -080058
59 /**
60 * Default constructor.
61 *
62 * @param appId application id
63 * @param packetService packet service
Thomas Vachuska220c61c2016-02-18 11:11:46 -080064 * @param hostService host service reference
Hyunsun Moon022272f2016-01-11 15:30:42 -080065 */
Hyunsun Moonb6febbe2016-02-12 15:59:53 -080066 public CordVtnArpProxy(ApplicationId appId, PacketService packetService, HostService hostService) {
Hyunsun Moon022272f2016-01-11 15:30:42 -080067 this.appId = appId;
68 this.packetService = packetService;
Hyunsun Moonb6febbe2016-02-12 15:59:53 -080069 this.hostService = hostService;
Hyunsun Moon022272f2016-01-11 15:30:42 -080070 }
71
72 /**
73 * Requests ARP packet.
74 */
75 public void requestPacket() {
76 TrafficSelector selector = DefaultTrafficSelector.builder()
77 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
78 .build();
79
80 packetService.requestPackets(selector,
81 PacketPriority.CONTROL,
82 appId,
83 Optional.empty());
84 }
85
86 /**
87 * Cancels ARP packet.
88 */
89 public void cancelPacket() {
90 TrafficSelector selector = DefaultTrafficSelector.builder()
91 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
92 .build();
93
94 packetService.cancelPackets(selector,
95 PacketPriority.CONTROL,
96 appId,
97 Optional.empty());
98 }
99
100 /**
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800101 * Adds a given gateway IP and MAC address to this ARP proxy.
Hyunsun Moon022272f2016-01-11 15:30:42 -0800102 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800103 * @param gatewayIp gateway ip address
104 * @param gatewayMac gateway mac address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800105 */
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800106 public void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
107 checkNotNull(gatewayIp);
108 checkNotNull(gatewayMac);
109 gateways.put(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon022272f2016-01-11 15:30:42 -0800110 }
111
112 /**
113 * Removes a given service IP address from this ARP proxy.
114 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800115 * @param gatewayIp gateway ip address
Hyunsun Moon022272f2016-01-11 15:30:42 -0800116 */
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800117 public void removeGateway(IpAddress gatewayIp) {
118 checkNotNull(gatewayIp);
119 gateways.remove(gatewayIp.getIp4Address());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800120 }
121
122 /**
123 * Emits ARP reply with fake MAC address for a given ARP request.
124 * It only handles requests for the registered service IPs, and the other
125 * requests can be handled by other ARP handlers like openstackSwitching or
126 * proxyArp, for example.
127 *
128 * @param context packet context
129 * @param ethPacket ethernet packet
130 */
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800131 public void processArpPacket(PacketContext context, Ethernet ethPacket) {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800132 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800133 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
Hyunsun Moon022272f2016-01-11 15:30:42 -0800134 return;
135 }
136
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800137 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800138
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800139 MacAddress gatewayMac = gateways.get(targetIp);
140 MacAddress replyMac = gatewayMac != null ? gatewayMac : getMacFromHostService(targetIp);
141
142 if (replyMac.equals(MacAddress.NONE)) {
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800143 log.debug("Failed to find MAC for {}", targetIp.toString());
Hyunsun Moon0d836e22016-02-01 23:30:58 -0800144 context.block();
145 return;
146 }
147
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800148 log.trace("Send ARP reply for {} with {}", targetIp.toString(), replyMac.toString());
Hyunsun Moon022272f2016-01-11 15:30:42 -0800149 Ethernet ethReply = ARP.buildArpReply(
150 targetIp,
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800151 replyMac,
Hyunsun Moon022272f2016-01-11 15:30:42 -0800152 ethPacket);
153
154 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
155 .setOutput(context.inPacket().receivedFrom().port())
156 .build();
157
158 packetService.emit(new DefaultOutboundPacket(
159 context.inPacket().receivedFrom().deviceId(),
160 treatment,
161 ByteBuffer.wrap(ethReply.serialize())));
162
163 context.block();
164 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800165
166 /**
167 * Emits gratuitous ARP when a gateway mac address has been changed.
168 *
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800169 * @param gatewayIp gateway ip address to update MAC
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700170 * @param instances set of instances to send gratuitous ARP packet
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800171 */
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700172 public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Instance> instances) {
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800173 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
174 if (gatewayMac == null) {
175 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
176 return;
177 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800178
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800179 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700180 instances.stream().forEach(instance -> {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800181 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700182 .setOutput(instance.portNumber())
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800183 .build();
184
185 packetService.emit(new DefaultOutboundPacket(
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700186 instance.deviceId(),
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800187 treatment,
188 ByteBuffer.wrap(ethArp.serialize())));
189 });
190 }
191
192 /**
193 * Builds gratuitous ARP packet with a given IP and MAC address.
194 *
195 * @param ip ip address for TPA and SPA
196 * @param mac new mac address
197 * @return ethernet packet
198 */
199 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
200 Ethernet eth = new Ethernet();
201
202 eth.setEtherType(Ethernet.TYPE_ARP);
203 eth.setSourceMACAddress(mac);
204 eth.setDestinationMACAddress(MacAddress.BROADCAST);
205
206 ARP arp = new ARP();
207 arp.setOpCode(ARP.OP_REQUEST);
208 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
209 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
210 arp.setProtocolType(ARP.PROTO_TYPE_IP);
211 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
212
213 arp.setSenderHardwareAddress(mac.toBytes());
214 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
215 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
216 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
217
218 eth.setPayload(arp);
219 return eth;
220 }
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800221
222 /**
223 * Returns MAC address of a host with a given target IP address by asking to
224 * host service. It does not support overlapping IP.
225 *
226 * @param targetIp target ip
227 * @return mac address, or NONE mac address if it fails to find the mac
228 */
229 private MacAddress getMacFromHostService(IpAddress targetIp) {
230 checkNotNull(targetIp);
231
232 Host host = hostService.getHostsByIp(targetIp)
233 .stream()
234 .findFirst()
235 .orElse(null);
236
237 if (host != null) {
Hyunsun Moon97c74192016-02-17 19:00:50 -0800238 log.trace("Found MAC from host service for {}", targetIp.toString());
Hyunsun Moonb6febbe2016-02-12 15:59:53 -0800239 return host.mac();
240 } else {
241 return MacAddress.NONE;
242 }
243 }
Hyunsun Moon022272f2016-01-11 15:30:42 -0800244}