blob: c0b1d61ddbeb57d00d6d3c9565c65d08f5b65f11 [file] [log] [blame]
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Hyunsun Moon42c7b4e2016-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 */
Hyunsun Moon7f4ed9d2016-04-14 16:13:42 -070016package org.onosproject.cordvtn.impl;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080017
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
Thomas Vachuska708d3032016-02-18 11:11:46 -080063 * @param hostService host service reference
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080064 */
Hyunsun Moon9cf43db2016-02-12 15:59:53 -080065 public CordVtnArpProxy(ApplicationId appId, PacketService packetService, HostService hostService) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080066 this.appId = appId;
67 this.packetService = packetService;
Hyunsun Moon9cf43db2016-02-12 15:59:53 -080068 this.hostService = hostService;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080069 }
70
71 /**
72 * Requests ARP packet.
73 */
74 public void requestPacket() {
75 TrafficSelector selector = DefaultTrafficSelector.builder()
76 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
77 .build();
78
79 packetService.requestPackets(selector,
80 PacketPriority.CONTROL,
81 appId,
82 Optional.empty());
83 }
84
85 /**
86 * Cancels ARP packet.
87 */
88 public void cancelPacket() {
89 TrafficSelector selector = DefaultTrafficSelector.builder()
90 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
91 .build();
92
93 packetService.cancelPackets(selector,
94 PacketPriority.CONTROL,
95 appId,
96 Optional.empty());
97 }
98
99 /**
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800100 * Adds a given gateway IP and MAC address to this ARP proxy.
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800101 *
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800102 * @param gatewayIp gateway ip address
103 * @param gatewayMac gateway mac address
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800104 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800105 public void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
106 checkNotNull(gatewayIp);
107 checkNotNull(gatewayMac);
108 gateways.put(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800109 }
110
111 /**
112 * Removes a given service IP address from this ARP proxy.
113 *
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800114 * @param gatewayIp gateway ip address
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800115 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800116 public void removeGateway(IpAddress gatewayIp) {
117 checkNotNull(gatewayIp);
118 gateways.remove(gatewayIp.getIp4Address());
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800119 }
120
121 /**
122 * Emits ARP reply with fake MAC address for a given ARP request.
123 * It only handles requests for the registered service IPs, and the other
124 * requests can be handled by other ARP handlers like openstackSwitching or
125 * proxyArp, for example.
126 *
127 * @param context packet context
128 * @param ethPacket ethernet packet
129 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800130 public void processArpPacket(PacketContext context, Ethernet ethPacket) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800131 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800132 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800133 return;
134 }
135
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800136 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800137
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800138 MacAddress gatewayMac = gateways.get(targetIp);
139 MacAddress replyMac = gatewayMac != null ? gatewayMac : getMacFromHostService(targetIp);
140
141 if (replyMac.equals(MacAddress.NONE)) {
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800142 log.debug("Failed to find MAC for {}", targetIp.toString());
Hyunsun Moon91866132016-02-01 23:30:58 -0800143 context.block();
144 return;
145 }
146
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800147 log.trace("Send ARP reply for {} with {}", targetIp.toString(), replyMac.toString());
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800148 Ethernet ethReply = ARP.buildArpReply(
149 targetIp,
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800150 replyMac,
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800151 ethPacket);
152
153 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
154 .setOutput(context.inPacket().receivedFrom().port())
155 .build();
156
157 packetService.emit(new DefaultOutboundPacket(
158 context.inPacket().receivedFrom().deviceId(),
159 treatment,
160 ByteBuffer.wrap(ethReply.serialize())));
161
162 context.block();
163 }
Hyunsun Moon746956f2016-01-24 21:47:06 -0800164
165 /**
166 * Emits gratuitous ARP when a gateway mac address has been changed.
167 *
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800168 * @param gatewayIp gateway ip address to update MAC
Hyunsun Moon746956f2016-01-24 21:47:06 -0800169 * @param hosts set of hosts to send gratuitous ARP packet
170 */
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800171 public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Host> hosts) {
172 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
173 if (gatewayMac == null) {
174 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
175 return;
176 }
Hyunsun Moon746956f2016-01-24 21:47:06 -0800177
Hyunsun Moonae39ae82016-02-17 15:02:06 -0800178 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
Hyunsun Moon746956f2016-01-24 21:47:06 -0800179 hosts.stream().forEach(host -> {
180 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
181 .setOutput(host.location().port())
182 .build();
183
184 packetService.emit(new DefaultOutboundPacket(
185 host.location().deviceId(),
186 treatment,
187 ByteBuffer.wrap(ethArp.serialize())));
188 });
189 }
190
191 /**
192 * Builds gratuitous ARP packet with a given IP and MAC address.
193 *
194 * @param ip ip address for TPA and SPA
195 * @param mac new mac address
196 * @return ethernet packet
197 */
198 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
199 Ethernet eth = new Ethernet();
200
201 eth.setEtherType(Ethernet.TYPE_ARP);
202 eth.setSourceMACAddress(mac);
203 eth.setDestinationMACAddress(MacAddress.BROADCAST);
204
205 ARP arp = new ARP();
206 arp.setOpCode(ARP.OP_REQUEST);
207 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
208 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
209 arp.setProtocolType(ARP.PROTO_TYPE_IP);
210 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
211
212 arp.setSenderHardwareAddress(mac.toBytes());
213 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
214 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
215 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
216
217 eth.setPayload(arp);
218 return eth;
219 }
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800220
221 /**
222 * Returns MAC address of a host with a given target IP address by asking to
223 * host service. It does not support overlapping IP.
224 *
225 * @param targetIp target ip
226 * @return mac address, or NONE mac address if it fails to find the mac
227 */
228 private MacAddress getMacFromHostService(IpAddress targetIp) {
229 checkNotNull(targetIp);
230
231 Host host = hostService.getHostsByIp(targetIp)
232 .stream()
233 .findFirst()
234 .orElse(null);
235
236 if (host != null) {
Hyunsun Moon2a225162016-02-17 19:00:50 -0800237 log.trace("Found MAC from host service for {}", targetIp.toString());
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800238 return host.mac();
239 } else {
240 return MacAddress.NONE;
241 }
242 }
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800243}