blob: 9d20e556941119b93839cde370a46cea29287a3f [file] [log] [blame]
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -08001/*
2 * Copyright 2014-2015 Open Networking Laboratory
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 */
16package org.onosproject.cordvtn;
17
Hyunsun Moonae39ae82016-02-17 15:02:06 -080018import com.google.common.collect.Maps;
Hyunsun Moon42c7b4e2016-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;
25import org.onosproject.core.ApplicationId;
Hyunsun Moon746956f2016-01-24 21:47:06 -080026import org.onosproject.net.Host;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080027import org.onosproject.net.flow.DefaultTrafficSelector;
28import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.TrafficSelector;
30import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon9cf43db2016-02-12 15:59:53 -080031import org.onosproject.net.host.HostService;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080032import org.onosproject.net.packet.DefaultOutboundPacket;
33import org.onosproject.net.packet.PacketContext;
34import org.onosproject.net.packet.PacketPriority;
35import org.onosproject.net.packet.PacketService;
36import org.slf4j.Logger;
37
38import java.nio.ByteBuffer;
Hyunsun Moonae39ae82016-02-17 15:02:06 -080039import java.util.Map;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080040import java.util.Optional;
41import java.util.Set;
42
43import static com.google.common.base.Preconditions.checkNotNull;
44import static org.slf4j.LoggerFactory.getLogger;
45
46/**
47 * Handles ARP requests for virtual network service IPs.
48 */
49public class CordVtnArpProxy {
50 protected final Logger log = getLogger(getClass());
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080051
52 private final ApplicationId appId;
53 private final PacketService packetService;
Hyunsun Moon9cf43db2016-02-12 15:59:53 -080054 private final HostService hostService;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080055
Hyunsun Moonae39ae82016-02-17 15:02:06 -080056 private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080057
58 /**
59 * Default constructor.
60 *
61 * @param appId application id
62 * @param packetService packet service
63 */
Hyunsun Moon9cf43db2016-02-12 15:59:53 -080064 public CordVtnArpProxy(ApplicationId appId, PacketService packetService, HostService hostService) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080065 this.appId = appId;
66 this.packetService = packetService;
Hyunsun Moon9cf43db2016-02-12 15:59:53 -080067 this.hostService = hostService;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080068 }
69
70 /**
71 * Requests ARP packet.
72 */
73 public void requestPacket() {
74 TrafficSelector selector = DefaultTrafficSelector.builder()
75 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
76 .build();
77
78 packetService.requestPackets(selector,
79 PacketPriority.CONTROL,
80 appId,
81 Optional.empty());
82 }
83
84 /**
85 * Cancels ARP packet.
86 */
87 public void cancelPacket() {
88 TrafficSelector selector = DefaultTrafficSelector.builder()
89 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
90 .build();
91
92 packetService.cancelPackets(selector,
93 PacketPriority.CONTROL,
94 appId,
95 Optional.empty());
96 }
97
98 /**
Hyunsun Moonae39ae82016-02-17 15:02:06 -080099 * Adds a given gateway IP and MAC address to this ARP proxy.
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800100 *
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800101 * @param gatewayIp gateway ip address
102 * @param gatewayMac gateway mac address
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800103 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800104 public void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
105 checkNotNull(gatewayIp);
106 checkNotNull(gatewayMac);
107 gateways.put(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800108 }
109
110 /**
111 * Removes a given service IP address from this ARP proxy.
112 *
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800113 * @param gatewayIp gateway ip address
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800114 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800115 public void removeGateway(IpAddress gatewayIp) {
116 checkNotNull(gatewayIp);
117 gateways.remove(gatewayIp.getIp4Address());
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800118 }
119
120 /**
121 * Emits ARP reply with fake MAC address for a given ARP request.
122 * It only handles requests for the registered service IPs, and the other
123 * requests can be handled by other ARP handlers like openstackSwitching or
124 * proxyArp, for example.
125 *
126 * @param context packet context
127 * @param ethPacket ethernet packet
128 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800129 public void processArpPacket(PacketContext context, Ethernet ethPacket) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800130 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800131 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800132 return;
133 }
134
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800135 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800136
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800137 MacAddress gatewayMac = gateways.get(targetIp);
138 MacAddress replyMac = gatewayMac != null ? gatewayMac : getMacFromHostService(targetIp);
139
140 if (replyMac.equals(MacAddress.NONE)) {
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800141 log.debug("Failed to find MAC for {}", targetIp.toString());
Hyunsun Moon91866132016-02-01 23:30:58 -0800142 context.block();
143 return;
144 }
145
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800146 log.trace("Send ARP reply for {} with {}", targetIp.toString(), replyMac.toString());
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800147 Ethernet ethReply = ARP.buildArpReply(
148 targetIp,
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800149 replyMac,
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800150 ethPacket);
151
152 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
153 .setOutput(context.inPacket().receivedFrom().port())
154 .build();
155
156 packetService.emit(new DefaultOutboundPacket(
157 context.inPacket().receivedFrom().deviceId(),
158 treatment,
159 ByteBuffer.wrap(ethReply.serialize())));
160
161 context.block();
162 }
Hyunsun Moon746956f2016-01-24 21:47:06 -0800163
164 /**
165 * Emits gratuitous ARP when a gateway mac address has been changed.
166 *
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800167 * @param gatewayIp gateway ip address to update MAC
Hyunsun Moon746956f2016-01-24 21:47:06 -0800168 * @param hosts set of hosts to send gratuitous ARP packet
169 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800170 public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Host> hosts) {
171 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
172 if (gatewayMac == null) {
173 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
174 return;
175 }
Hyunsun Moon746956f2016-01-24 21:47:06 -0800176
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800177 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon746956f2016-01-24 21:47:06 -0800178 hosts.stream().forEach(host -> {
179 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
180 .setOutput(host.location().port())
181 .build();
182
183 packetService.emit(new DefaultOutboundPacket(
184 host.location().deviceId(),
185 treatment,
186 ByteBuffer.wrap(ethArp.serialize())));
187 });
188 }
189
190 /**
191 * Builds gratuitous ARP packet with a given IP and MAC address.
192 *
193 * @param ip ip address for TPA and SPA
194 * @param mac new mac address
195 * @return ethernet packet
196 */
197 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
198 Ethernet eth = new Ethernet();
199
200 eth.setEtherType(Ethernet.TYPE_ARP);
201 eth.setSourceMACAddress(mac);
202 eth.setDestinationMACAddress(MacAddress.BROADCAST);
203
204 ARP arp = new ARP();
205 arp.setOpCode(ARP.OP_REQUEST);
206 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
207 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
208 arp.setProtocolType(ARP.PROTO_TYPE_IP);
209 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
210
211 arp.setSenderHardwareAddress(mac.toBytes());
212 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
213 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
214 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
215
216 eth.setPayload(arp);
217 return eth;
218 }
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800219
220 /**
221 * Returns MAC address of a host with a given target IP address by asking to
222 * host service. It does not support overlapping IP.
223 *
224 * @param targetIp target ip
225 * @return mac address, or NONE mac address if it fails to find the mac
226 */
227 private MacAddress getMacFromHostService(IpAddress targetIp) {
228 checkNotNull(targetIp);
229
230 Host host = hostService.getHostsByIp(targetIp)
231 .stream()
232 .findFirst()
233 .orElse(null);
234
235 if (host != null) {
236 log.debug("Found MAC from host service for {}", targetIp.toString());
237 return host.mac();
238 } else {
239 return MacAddress.NONE;
240 }
241 }
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800242}