blob: feee5a76d0089c3390cecea12e2e0036149c3840 [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
18import com.google.common.collect.Sets;
19import 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;
39import java.util.Optional;
40import java.util.Set;
41
Hyunsun Moon746956f2016-01-24 21:47:06 -080042import static com.google.common.base.Preconditions.checkArgument;
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -080043import 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
56 private Set<Ip4Address> serviceIPs = Sets.newHashSet();
57
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 /**
99 * Adds a given service IP address to be served.
100 *
101 * @param serviceIp service ip
102 */
103 public void addServiceIp(IpAddress serviceIp) {
104 checkNotNull(serviceIp);
105 serviceIPs.add(serviceIp.getIp4Address());
106 }
107
108 /**
109 * Removes a given service IP address from this ARP proxy.
110 *
111 * @param serviceIp service ip
112 */
113 public void removeServiceIp(IpAddress serviceIp) {
114 checkNotNull(serviceIp);
115 serviceIPs.remove(serviceIp.getIp4Address());
116 }
117
118 /**
119 * Emits ARP reply with fake MAC address for a given ARP request.
120 * It only handles requests for the registered service IPs, and the other
121 * requests can be handled by other ARP handlers like openstackSwitching or
122 * proxyArp, for example.
123 *
124 * @param context packet context
125 * @param ethPacket ethernet packet
Hyunsun Moon746956f2016-01-24 21:47:06 -0800126 * @param gatewayMac gateway mac address
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800127 */
Hyunsun Moon746956f2016-01-24 21:47:06 -0800128 public void processArpPacket(PacketContext context, Ethernet ethPacket, MacAddress gatewayMac) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800129 ARP arpPacket = (ARP) ethPacket.getPayload();
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800130 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800131 return;
132 }
133
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800134 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
135 MacAddress macAddr = serviceIPs.contains(targetIp) ?
136 gatewayMac : getMacFromHostService(targetIp);
137
138 if (macAddr.equals(MacAddress.NONE)) {
139 log.debug("Failed to find MAC for {}", targetIp.toString());
Hyunsun Moon91866132016-02-01 23:30:58 -0800140 context.block();
141 return;
142 }
143
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800144 Ethernet ethReply = ARP.buildArpReply(
145 targetIp,
Hyunsun Moon27039802016-02-16 16:20:12 -0800146 macAddr,
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800147 ethPacket);
148
149 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
150 .setOutput(context.inPacket().receivedFrom().port())
151 .build();
152
153 packetService.emit(new DefaultOutboundPacket(
154 context.inPacket().receivedFrom().deviceId(),
155 treatment,
156 ByteBuffer.wrap(ethReply.serialize())));
157
158 context.block();
159 }
Hyunsun Moon746956f2016-01-24 21:47:06 -0800160
161 /**
162 * Emits gratuitous ARP when a gateway mac address has been changed.
163 *
164 * @param ip ip address to update MAC
165 * @param mac new mac address
166 * @param hosts set of hosts to send gratuitous ARP packet
167 */
168 public void sendGratuitousArp(IpAddress ip, MacAddress mac, Set<Host> hosts) {
169 checkArgument(!mac.equals(MacAddress.NONE));
170
171 Ethernet ethArp = buildGratuitousArp(ip.getIp4Address(), mac);
172 hosts.stream().forEach(host -> {
173 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
174 .setOutput(host.location().port())
175 .build();
176
177 packetService.emit(new DefaultOutboundPacket(
178 host.location().deviceId(),
179 treatment,
180 ByteBuffer.wrap(ethArp.serialize())));
181 });
182 }
183
184 /**
185 * Builds gratuitous ARP packet with a given IP and MAC address.
186 *
187 * @param ip ip address for TPA and SPA
188 * @param mac new mac address
189 * @return ethernet packet
190 */
191 private Ethernet buildGratuitousArp(IpAddress ip, MacAddress mac) {
192 Ethernet eth = new Ethernet();
193
194 eth.setEtherType(Ethernet.TYPE_ARP);
195 eth.setSourceMACAddress(mac);
196 eth.setDestinationMACAddress(MacAddress.BROADCAST);
197
198 ARP arp = new ARP();
199 arp.setOpCode(ARP.OP_REQUEST);
200 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
201 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
202 arp.setProtocolType(ARP.PROTO_TYPE_IP);
203 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
204
205 arp.setSenderHardwareAddress(mac.toBytes());
206 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
207 arp.setSenderProtocolAddress(ip.getIp4Address().toOctets());
208 arp.setTargetProtocolAddress(ip.getIp4Address().toOctets());
209
210 eth.setPayload(arp);
211 return eth;
212 }
Hyunsun Moon9cf43db2016-02-12 15:59:53 -0800213
214 /**
215 * Returns MAC address of a host with a given target IP address by asking to
216 * host service. It does not support overlapping IP.
217 *
218 * @param targetIp target ip
219 * @return mac address, or NONE mac address if it fails to find the mac
220 */
221 private MacAddress getMacFromHostService(IpAddress targetIp) {
222 checkNotNull(targetIp);
223
224 Host host = hostService.getHostsByIp(targetIp)
225 .stream()
226 .findFirst()
227 .orElse(null);
228
229 if (host != null) {
230 log.debug("Found MAC from host service for {}", targetIp.toString());
231 return host.mac();
232 } else {
233 return MacAddress.NONE;
234 }
235 }
Hyunsun Moon42c7b4e2016-01-11 15:30:42 -0800236}