blob: cfdd9c1f065d70556b1458a2a0a07b74d9da6e5b [file] [log] [blame]
Jonathan Hart0961fe82014-04-03 09:56:25 -07001package net.onrc.onos.apps.proxyarp;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12002
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12003import java.net.InetAddress;
4import java.net.UnknownHostException;
5import java.util.ArrayList;
6import java.util.Collection;
Jonathan Harte93aed42013-12-05 18:39:50 -08007import java.util.HashMap;
Jonathan Hart6261dcd2013-07-22 17:58:35 +12008import java.util.Iterator;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12009import java.util.List;
10import java.util.Map;
11import java.util.Set;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120012import java.util.Timer;
13import java.util.TimerTask;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120014
Jonathan Harte93aed42013-12-05 18:39:50 -080015import net.floodlightcontroller.core.module.FloodlightModuleContext;
16import net.floodlightcontroller.core.module.IFloodlightModule;
17import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart5afde492013-10-01 12:30:53 +130018import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120019import net.floodlightcontroller.util.MACAddress;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070020import net.onrc.onos.api.packet.IPacketListener;
21import net.onrc.onos.api.packet.IPacketService;
Yuta HIGUCHI238fa2a2014-05-01 09:56:46 -070022import net.onrc.onos.apps.proxyarp.web.ArpWebRoutable;
Jonathan Hart8f6dc092014-04-18 15:56:43 -070023import net.onrc.onos.apps.sdnip.Interface;
Jonathan Hart6df90172014-04-03 10:13:11 -070024import net.onrc.onos.core.datagrid.IDatagridService;
25import net.onrc.onos.core.datagrid.IEventChannel;
26import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070027import net.onrc.onos.core.main.config.IConfigInfoService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070028import net.onrc.onos.core.packet.ARP;
29import net.onrc.onos.core.packet.Ethernet;
30import net.onrc.onos.core.packet.IPv4;
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -070031import net.onrc.onos.core.topology.Host;
Jonathan Harte37e4e22014-05-13 19:12:02 -070032import net.onrc.onos.core.topology.ITopologyService;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070033import net.onrc.onos.core.topology.Port;
Jonathan Hart472062d2014-04-03 10:56:48 -070034import net.onrc.onos.core.topology.Switch;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070035import net.onrc.onos.core.topology.MutableTopology;
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -070036import net.onrc.onos.core.util.Dpid;
37import net.onrc.onos.core.util.PortNumber;
Jonathan Hart23701d12014-04-03 10:45:48 -070038import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120039
Jonathan Hartc78b8f62014-08-07 22:31:09 -070040import org.projectfloodlight.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
Jonathan Hart4dfc3652013-08-02 20:22:36 +120044import com.google.common.collect.HashMultimap;
45import com.google.common.collect.Multimaps;
46import com.google.common.collect.SetMultimap;
Jonathan Hart55316582014-05-09 10:02:58 -070047import com.google.common.net.InetAddresses;
Jonathan Hart4dfc3652013-08-02 20:22:36 +120048
Jonathan Hart51dc5e12014-04-22 11:03:59 -070049public class ProxyArpManager implements IProxyArpService, IFloodlightModule,
50 IPacketListener {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070051 private static final Logger log = LoggerFactory
52 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080053
Jonathan Hart55316582014-05-09 10:02:58 -070054 private static long arpTimerPeriodConfig = 2000; // ms
TeruU3c049c42014-04-15 10:13:25 -070055 private static int arpRequestTimeoutConfig = 2000; // ms
56 private long arpCleaningTimerPeriodConfig = 60 * 1000; // ms (1 min)
Jonathan Hartda4d0e12013-09-30 21:00:20 +130057
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070058 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070059 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
TeruU3c049c42014-04-15 10:13:25 -070060 private IEventChannel<String, ArpCacheNotification> arpCacheEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070061 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU3c049c42014-04-15 10:13:25 -070062 private static final String ARP_CACHE_CHANNEL_NAME = "onos.arp_cache";
63 private final ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
TeruU3c049c42014-04-15 10:13:25 -070064 private final ArpCacheEventHandler arpCacheEventHandler = new ArpCacheEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070065
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070066 private IConfigInfoService configService;
67 private IRestApiService restApi;
Ray Milkey269ffb92014-04-03 14:43:30 -070068
Jonathan Harte37e4e22014-05-13 19:12:02 -070069 private ITopologyService topologyService;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070070 private MutableTopology mutableTopology;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070071 private IPacketService packetService;
Jonathan Harte93aed42013-12-05 18:39:50 -080072
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070073 private short vlan;
74 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080075
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070076 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120077
TeruU3c049c42014-04-15 10:13:25 -070078 private ArpCache arpCache;
79
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070080 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070081 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070082
Ray Milkey269ffb92014-04-03 14:43:30 -070083 @Override
84 public void entryAdded(ArpReplyNotification arpReply) {
85 log.debug("Received ARP reply notification for ip {}, mac {}",
86 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070087
Jonathan Hart55316582014-05-09 10:02:58 -070088 InetAddress addr = InetAddresses.fromInteger(arpReply.getTargetAddress());
89
90 sendArpReplyToWaitingRequesters(addr,
91 arpReply.getTargetMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -070092 }
93
94 @Override
95 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -070096 entryAdded(arpReply);
97 }
98
99 @Override
100 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -0700101 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
102 }
103 }
104
105 private class ArpCacheEventHandler implements
106 IEventChannelListener<String, ArpCacheNotification> {
107
108 /**
109 * Startup processing.
110 */
111 private void startUp() {
112 //
113 // TODO: Read all state from the database:
114 // For now, as a shortcut we read it from the datagrid
115 //
116 Collection<ArpCacheNotification> arpCacheEvents =
117 arpCacheEventChannel.getAllEntries();
118
119 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
120 entryAdded(arpCacheEvent);
121 }
122 }
123
124 @Override
125 public void entryAdded(ArpCacheNotification value) {
TeruU3c049c42014-04-15 10:13:25 -0700126 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700127 InetAddress targetIpAddress =
128 InetAddress.getByAddress(value.getTargetAddress());
129
130 log.debug("Received entryAdded for ARP cache notification " +
131 "for ip {}, mac {}", targetIpAddress, value.getTargetMacAddress());
132 arpCache.update(targetIpAddress,
133 MACAddress.valueOf(value.getTargetMacAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700134 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700135 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700136 }
137 }
138
139 @Override
140 public void entryRemoved(ArpCacheNotification value) {
141 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
142 value.getTargetAddress(), value.getTargetMacAddress());
143 try {
144 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
145 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700146 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700147 }
148 }
149
150 @Override
151 public void entryUpdated(ArpCacheNotification value) {
152 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700153 InetAddress targetIpAddress =
154 InetAddress.getByAddress(value.getTargetAddress());
155
156 log.debug("Received entryUpdated for ARP cache notification " +
157 "for ip {}, mac {}", targetIpAddress, value.getTargetMacAddress());
158 arpCache.update(targetIpAddress,
159 MACAddress.valueOf(value.getTargetMacAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700160 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700161 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700162 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700163 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700164 }
165
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700166 private static class ArpRequest {
167 private final IArpRequester requester;
168 private final boolean retry;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700169 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200170
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700171 public ArpRequest(IArpRequester requester, boolean retry) {
172 this.requester = requester;
173 this.retry = retry;
Jonathan Hart55316582014-05-09 10:02:58 -0700174
175 requestTime = System.currentTimeMillis();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700176 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200177
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700178 public ArpRequest(ArpRequest old) {
179 this.requester = old.requester;
180 this.retry = old.retry;
181 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200182
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700183 public boolean isExpired() {
Jonathan Hart55316582014-05-09 10:02:58 -0700184 return ((System.currentTimeMillis() - requestTime)
185 > arpRequestTimeoutConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700186 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200187
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700188 public boolean shouldRetry() {
189 return retry;
190 }
191
192 public void dispatchReply(InetAddress ipAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700194 requester.arpResponse(ipAddress, replyMacAddress);
195 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700196 }
197
198 private class HostArpRequester implements IArpRequester {
199 private final ARP arpRequest;
200 private final long dpid;
201 private final short port;
202
203 public HostArpRequester(ARP arpRequest, long dpid, short port) {
204 this.arpRequest = arpRequest;
205 this.dpid = dpid;
206 this.port = port;
207 }
208
209 @Override
210 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
211 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
212 macAddress);
213 }
214 }
215
216 @Override
217 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
218 Collection<Class<? extends IFloodlightService>> l =
219 new ArrayList<Class<? extends IFloodlightService>>();
220 l.add(IProxyArpService.class);
221 return l;
222 }
223
224 @Override
225 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
226 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
227 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
228 m.put(IProxyArpService.class, this);
229 return m;
230 }
231
232 @Override
233 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
234 Collection<Class<? extends IFloodlightService>> dependencies =
235 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700236 dependencies.add(IRestApiService.class);
237 dependencies.add(IDatagridService.class);
238 dependencies.add(IConfigInfoService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700239 dependencies.add(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700240 dependencies.add(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700241 return dependencies;
242 }
243
244 @Override
245 public void init(FloodlightModuleContext context) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700246 this.configService = context.getServiceImpl(IConfigInfoService.class);
247 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700248 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700249 this.topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700250 this.packetService = context.getServiceImpl(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700251
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700252 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
253 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700254 }
255
256 @Override
257 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700258 Map<String, String> configOptions = context.getConfigParams(this);
259
TeruU8b2d1672014-04-25 17:02:56 -0700260 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700261 arpCleaningTimerPeriodConfig =
262 Long.parseLong(configOptions.get("cleanupmsec"));
TeruU8b2d1672014-04-25 17:02:56 -0700263 } catch (NumberFormatException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700264 log.debug("ArpCleaningTimerPeriod related config options were " +
265 "not set. Using default.");
TeruU8b2d1672014-04-25 17:02:56 -0700266 }
267
Jonathan Hart4aa47532014-04-23 15:23:41 -0700268 Long agingmsec = null;
269 try {
270 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
271 } catch (NumberFormatException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700272 log.debug("ArpEntryTimeout related config options were " +
273 "not set. Using default.");
Jonathan Hart4aa47532014-04-23 15:23:41 -0700274 }
275
276 arpCache = new ArpCache();
277 if (agingmsec != null) {
278 arpCache.setArpEntryTimeoutConfig(agingmsec);
279 }
280
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700281 this.vlan = configService.getVlan();
282 log.info("vlan set to {}", this.vlan);
283
284 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700285 packetService.registerPacketListener(this);
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700286 mutableTopology = topologyService.getTopology();
Ray Milkey269ffb92014-04-03 14:43:30 -0700287
288 //
289 // Event notification setup: channels and event handlers
290 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700291
292 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
293 arpReplyEventHandler,
294 Long.class,
295 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700296
TeruU3c049c42014-04-15 10:13:25 -0700297 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
298 arpCacheEventHandler,
299 String.class,
300 ArpCacheNotification.class);
301 arpCacheEventHandler.startUp();
302
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700303 Timer arpTimer = new Timer("arp-processing");
304 arpTimer.scheduleAtFixedRate(new TimerTask() {
305 @Override
306 public void run() {
307 doPeriodicArpProcessing();
308 }
TeruU3c049c42014-04-15 10:13:25 -0700309 }, 0, arpTimerPeriodConfig);
310
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700311 Timer arpCacheTimer = new Timer("arp-cleaning");
TeruU3c049c42014-04-15 10:13:25 -0700312 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
313 @Override
314 public void run() {
315 doPeriodicArpCleaning();
316 }
317 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700318 }
319
320 /*
321 * Function that runs periodically to manage the asynchronous request mechanism.
322 * It basically cleans up old ARP requests if we don't get a response for them.
323 * The caller can designate that a request should be retried indefinitely, and
324 * this task will handle that as well.
325 */
326 private void doPeriodicArpProcessing() {
327 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
328 .<InetAddress, ArpRequest>create();
329
Jonathan Hart55316582014-05-09 10:02:58 -0700330 // We must synchronize externally on the Multimap while using an
331 // iterator, even though it's a synchronizedMultimap
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700332 synchronized (arpRequests) {
333 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
334 .entries().iterator();
335
336 while (it.hasNext()) {
337 Map.Entry<InetAddress, ArpRequest> entry = it.next();
338 ArpRequest request = entry.getValue();
339 if (request.isExpired()) {
340 log.debug("Cleaning expired ARP request for {}", entry
341 .getKey().getHostAddress());
342
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700343 it.remove();
344
345 if (request.shouldRetry()) {
346 retryList.put(entry.getKey(), request);
347 }
348 }
349 }
350 }
351
352 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
353 .asMap().entrySet()) {
354
355 InetAddress address = entry.getKey();
356
357 log.debug("Resending ARP request for {}", address.getHostAddress());
358
359 // Only ARP requests sent by the controller will have the retry flag
360 // set, so for now we can just send a new ARP request for that
361 // address.
362 sendArpRequestForAddress(address);
363
364 for (ArpRequest request : entry.getValue()) {
365 arpRequests.put(address, new ArpRequest(request));
366 }
367 }
368 }
369
370 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700371 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruU8b2d1672014-04-25 17:02:56 -0700372
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700373 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
374 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700375 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700376 if (arp.getOpCode() == ARP.OP_REQUEST) {
Yuta HIGUCHI9da3a6e2014-06-10 22:11:58 -0700377 handleArpRequest(sw.getDpid().value(), inPort.getNumber().shortValue(),
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700378 arp, eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700379 } else if (arp.getOpCode() == ARP.OP_REPLY) {
380 // For replies we simply send a notification via Hazelcast
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700381 sendArpReplyNotification(eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700382 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700383 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700384 }
pingping-linb8757bf2013-12-13 01:48:58 +0800385
TeruU3c049c42014-04-15 10:13:25 -0700386 private void learnArp(ARP arp) {
387 ArpCacheNotification arpCacheNotification = null;
388
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700389 arpCacheNotification = new ArpCacheNotification(
390 arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
TeruU3c049c42014-04-15 10:13:25 -0700391
392 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700393 arpCacheEventChannel.addEntry(InetAddress.getByAddress(
394 arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
TeruU3c049c42014-04-15 10:13:25 -0700395 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700396 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700397 }
398 }
399
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700400 private void handleArpRequest(long dpid, short inPort, ARP arp, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700401 if (log.isTraceEnabled()) {
402 log.trace("ARP request received for {}",
403 inetAddressToString(arp.getTargetProtocolAddress()));
404 }
pingping-linb8757bf2013-12-13 01:48:58 +0800405
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700406 InetAddress target;
407 try {
408 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
409 } catch (UnknownHostException e) {
410 log.debug("Invalid address in ARP request", e);
411 return;
412 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700413
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700414 if (configService.fromExternalNetwork(dpid, inPort)) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700415 // If the request came from outside our network, we only care if
416 // it was a request for one of our interfaces.
417 if (configService.isInterfaceAddress(target)) {
418 log.trace(
419 "ARP request for our interface. Sending reply {} => {}",
420 target.getHostAddress(),
421 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800422
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700423 sendArpReply(arp, dpid, inPort,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700424 configService.getRouterMacAddress());
425 }
pingping-linb8757bf2013-12-13 01:48:58 +0800426
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700427 return;
428 }
pingping-linb8757bf2013-12-13 01:48:58 +0800429
TeruU3c049c42014-04-15 10:13:25 -0700430 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800431
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700432 arpRequests.put(target, new ArpRequest(
433 new HostArpRequester(arp, dpid, inPort), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800434
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700435 mutableTopology.acquireReadLock();
436 Host targetHost = mutableTopology.getHostByMac(
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700437 MACAddress.valueOf(arp.getTargetHardwareAddress()));
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700438 mutableTopology.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800439
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700440 if (targetHost == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700441 if (log.isTraceEnabled()) {
442 log.trace("No device info found for {} - broadcasting",
443 target.getHostAddress());
444 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800445
Ray Milkey269ffb92014-04-03 14:43:30 -0700446 // We don't know the device so broadcast the request out
Jonathan Harte3702f22014-04-29 02:56:56 -0700447 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700448 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700449 } else {
450 // Even if the device exists in our database, we do not reply to
451 // the request directly, but check whether the device is still valid
452 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200453
Ray Milkey269ffb92014-04-03 14:43:30 -0700454 if (log.isTraceEnabled()) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700455 log.trace("The target Host Record in DB is: {} => {} " +
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700456 "from ARP request host at {}/{}", new Object[]{
Ray Milkey269ffb92014-04-03 14:43:30 -0700457 inetAddressToString(arp.getTargetProtocolAddress()),
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700458 macAddress, HexString.toHexString(dpid), inPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700459 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200460
Ray Milkey269ffb92014-04-03 14:43:30 -0700461 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800462
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700463 Iterable<Port> outPorts = targetHost.getAttachmentPoints();
Ray Milkey269ffb92014-04-03 14:43:30 -0700464
465 if (!outPorts.iterator().hasNext()) {
466 if (log.isTraceEnabled()) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700467 log.trace("Host {} exists but is not connected to any ports" +
Ray Milkey269ffb92014-04-03 14:43:30 -0700468 " - broadcasting", macAddress);
469 }
470
Jonathan Harte3702f22014-04-29 02:56:56 -0700471 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700472 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700473 } else {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700474 for (Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700475
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700476 if (portObject.getOutgoingLink() != null ||
477 portObject.getIncomingLink() != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700478 continue;
479 }
480
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700481 PortNumber outPort = portObject.getNumber();
Ray Milkey269ffb92014-04-03 14:43:30 -0700482 Switch outSwitchObject = portObject.getSwitch();
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700483 Dpid outSwitch = outSwitchObject.getDpid();
Ray Milkey269ffb92014-04-03 14:43:30 -0700484
485 if (log.isTraceEnabled()) {
486 log.trace("Probing device {} on port {}/{}",
487 new Object[]{macAddress,
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700488 outSwitch, outPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 }
490
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700491 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700492 eth, new SwitchPort(outSwitch, outPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700493 }
494 }
495 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700496 }
pingping-linb8757bf2013-12-13 01:48:58 +0800497
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700498 // TODO this method has not been tested after recent implementation changes.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700499 private void sendArpRequestForAddress(InetAddress ipAddress) {
500 // TODO what should the sender IP address and MAC address be if no
501 // IP addresses are configured? Will there ever be a need to send
502 // ARP requests from the controller in that case?
503 // All-zero MAC address doesn't seem to work - hosts don't respond to it
504
505 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
506 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
507 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
508 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
509 (byte) 0xff, (byte) 0xff, (byte) 0xff};
510
511 ARP arpRequest = new ARP();
512
513 arpRequest
514 .setHardwareType(ARP.HW_TYPE_ETHERNET)
515 .setProtocolType(ARP.PROTO_TYPE_IP)
516 .setHardwareAddressLength(
517 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
518 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
519 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
520 .setTargetProtocolAddress(ipAddress.getAddress());
521
522 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700523 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700524 byte[] senderMacAddress = genericNonZeroMac;
525 if (routerMacAddress != null) {
526 senderMacAddress = routerMacAddress.toBytes();
527 }
528 arpRequest.setSenderHardwareAddress(senderMacAddress);
529
530 byte[] senderIPAddress = zeroIpv4;
531 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700532 if (intf == null) {
533 // TODO handle the case where the controller needs to send an ARP
534 // request but there's not IP configuration. In this case the
535 // request should be broadcast out all edge ports in the network.
pingping-lin6caee5f2014-08-25 11:39:32 -0700536 log.warn("Cannot send ARP: there is no outgoing interface in the "
537 + "configuration");
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700538 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700539 }
540
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700541 senderIPAddress = intf.getIpAddress().getAddress();
542
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700543 arpRequest.setSenderProtocolAddress(senderIPAddress);
544
545 Ethernet eth = new Ethernet();
546 eth.setSourceMACAddress(senderMacAddress)
547 .setDestinationMACAddress(broadcastMac)
548 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
549
550 if (vlan != NO_VLAN) {
551 eth.setVlanID(vlan).setPriorityCode((byte) 0);
552 }
553
554 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700555
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700556 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700557 eth, new SwitchPort(intf.getDpid(), intf.getPort()));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700558 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700559
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700560 // Please leave it for now because this code is needed for SDN-IP.
561 // It will be removed soon.
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700562 /*
TeruU7feef8a2014-04-03 00:15:49 -0700563 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700564 sendArpRequestToSwitches(dstAddress, arpRequest,
565 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700566 }
567
TeruU3c049c42014-04-15 10:13:25 -0700568 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
569 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700570
571 if (configService.hasLayer3Configuration()) {
572 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700573 if (intf != null) {
574 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
575 }
576 else {
577 //TODO here it should be broadcast out all non-interface edge ports.
578 //I think we can assume that if it's not a request for an external
579 //network, it's an ARP for a host in our own network. So we want to
580 //send it out all edge ports that don't have an interface configured
581 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700582 log.debug("No interface found to send ARP request for {}",
583 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700584 }
TeruU3c049c42014-04-15 10:13:25 -0700585 }
586 else {
587 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700588 }
589 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700590 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700591
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700592 private void sendArpReplyNotification(Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700593 ARP arp = (ARP) eth.getPayload();
594
595 if (log.isTraceEnabled()) {
596 log.trace("Sending ARP reply for {} to other ONOS instances",
597 inetAddressToString(arp.getSenderProtocolAddress()));
598 }
599
600 InetAddress targetAddress;
601
602 try {
603 targetAddress = InetAddress.getByAddress(arp
604 .getSenderProtocolAddress());
605 } catch (UnknownHostException e) {
606 log.error("Unknown host", e);
607 return;
608 }
609
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700610 int intAddress = InetAddresses.coerceToInteger(targetAddress);
611
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700612 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
613
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700614 ArpReplyNotification value = new ArpReplyNotification(intAddress, mac);
615
616 log.debug("ArpReplyNotification ip {}, mac {}", intAddress, mac);
TeruU3c049c42014-04-15 10:13:25 -0700617 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700618 }
619
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700620 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700621 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700622 if (log.isTraceEnabled()) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700623 log.trace("Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700624 new Object[]{
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700625 inetAddressToString(arpRequest.getTargetProtocolAddress()),
626 targetMac, inetAddressToString(arpRequest
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700627 .getSenderProtocolAddress())});
628 }
629
630 ARP arpReply = new ARP();
631 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
632 .setProtocolType(ARP.PROTO_TYPE_IP)
633 .setHardwareAddressLength(
634 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
635 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
636 .setOpCode(ARP.OP_REPLY)
637 .setSenderHardwareAddress(targetMac.toBytes())
638 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
639 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
640 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
641
642 Ethernet eth = new Ethernet();
643 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
644 .setSourceMACAddress(targetMac.toBytes())
645 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
646
647 if (vlan != NO_VLAN) {
648 eth.setVlanID(vlan).setPriorityCode((byte) 0);
649 }
650
Jonathan Harte3702f22014-04-29 02:56:56 -0700651 packetService.sendPacket(eth, new SwitchPort(dpid, port));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700652 }
653
654 private String inetAddressToString(byte[] bytes) {
655 try {
656 return InetAddress.getByAddress(bytes).getHostAddress();
657 } catch (UnknownHostException e) {
658 log.debug("Invalid IP address", e);
659 return "";
660 }
661 }
662
663 /*
664 * IProxyArpService methods
665 */
666
667 @Override
668 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700669 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700670 }
671
672 @Override
673 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700674 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700675 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
676
677 // Sanity check to make sure we don't send a request for our own address
678 if (!configService.isInterfaceAddress(ipAddress)) {
679 sendArpRequestForAddress(ipAddress);
680 }
681 }
682
683 @Override
684 public List<String> getMappings() {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700685 return arpCache.getMappings();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700686 }
687
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700688 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700689 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700690 log.debug("Sending ARP reply for {} to requesters",
691 address.getHostAddress());
692
693 // See if anyone's waiting for this ARP reply
694 Set<ArpRequest> requests = arpRequests.get(address);
695
696 // Synchronize on the Multimap while using an iterator for one of the
697 // sets
698 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
699 requests.size());
700 synchronized (arpRequests) {
701 Iterator<ArpRequest> it = requests.iterator();
702 while (it.hasNext()) {
703 ArpRequest request = it.next();
704 it.remove();
705 requestsToSend.add(request);
706 }
707 }
708
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700709 // Don't hold an ARP lock while dispatching requests
710 for (ArpRequest request : requestsToSend) {
711 request.dispatchReply(address, mac);
712 }
713 }
TeruU3c049c42014-04-15 10:13:25 -0700714
715 private void doPeriodicArpCleaning() {
716 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
717 for (InetAddress expireIp : expiredipslist) {
718 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
719 arpCacheEventChannel.removeEntry(expireIp.toString());
720 }
721 }
TeruU8b2d1672014-04-25 17:02:56 -0700722
723 public long getArpEntryTimeout() {
724 return arpCache.getArpEntryTimeout();
725 }
726
727 public long getArpCleaningTimerPeriod() {
728 return arpCleaningTimerPeriodConfig;
729 }
Yuta HIGUCHI33a04972014-06-03 13:00:15 -0700730
731 /**
732 * Replaces the internal ArpCache.
733 *
734 * @param cache ArpCache instance
735 *
736 * @exclude Backdoor for unit testing purpose only, do not use.
737 */
738 void debugReplaceArpCache(final ArpCache cache) {
739 this.arpCache = cache;
740 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700741}