blob: 04957f2934fad1b9920e3d1ae00c7b9aaec206d9 [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;
Jonathan Hart472062d2014-04-03 10:56:48 -070031import net.onrc.onos.core.topology.Device;
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;
Jonathan Hart55316582014-05-09 10:02:58 -070035import net.onrc.onos.core.topology.Topology;
Jonathan Hart23701d12014-04-03 10:45:48 -070036import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120037
Jonathan Hart8ec133c2013-06-26 15:25:18 +120038import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Jonathan Hart4dfc3652013-08-02 20:22:36 +120042import com.google.common.collect.HashMultimap;
43import com.google.common.collect.Multimaps;
44import com.google.common.collect.SetMultimap;
Jonathan Hart55316582014-05-09 10:02:58 -070045import com.google.common.net.InetAddresses;
Jonathan Hart4dfc3652013-08-02 20:22:36 +120046
Jonathan Hart51dc5e12014-04-22 11:03:59 -070047public class ProxyArpManager implements IProxyArpService, IFloodlightModule,
48 IPacketListener {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070049 private static final Logger log = LoggerFactory
50 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080051
Jonathan Hart55316582014-05-09 10:02:58 -070052 private static long arpTimerPeriodConfig = 2000; // ms
TeruU3c049c42014-04-15 10:13:25 -070053 private static int arpRequestTimeoutConfig = 2000; // ms
54 private long arpCleaningTimerPeriodConfig = 60 * 1000; // ms (1 min)
Jonathan Hartda4d0e12013-09-30 21:00:20 +130055
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070056 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070057 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
TeruU3c049c42014-04-15 10:13:25 -070058 private IEventChannel<String, ArpCacheNotification> arpCacheEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070059 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU3c049c42014-04-15 10:13:25 -070060 private static final String ARP_CACHE_CHANNEL_NAME = "onos.arp_cache";
61 private final ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
TeruU3c049c42014-04-15 10:13:25 -070062 private final ArpCacheEventHandler arpCacheEventHandler = new ArpCacheEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070063
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070064 private IConfigInfoService configService;
65 private IRestApiService restApi;
Ray Milkey269ffb92014-04-03 14:43:30 -070066
Jonathan Harte37e4e22014-05-13 19:12:02 -070067 private ITopologyService topologyService;
68 private Topology topology;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070069 private IPacketService packetService;
Jonathan Harte93aed42013-12-05 18:39:50 -080070
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070071 private short vlan;
72 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080073
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070074 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120075
TeruU3c049c42014-04-15 10:13:25 -070076 private ArpCache arpCache;
77
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070078 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070079 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070080
Ray Milkey269ffb92014-04-03 14:43:30 -070081 @Override
82 public void entryAdded(ArpReplyNotification arpReply) {
83 log.debug("Received ARP reply notification for ip {}, mac {}",
84 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070085
Jonathan Hart55316582014-05-09 10:02:58 -070086 InetAddress addr = InetAddresses.fromInteger(arpReply.getTargetAddress());
87
88 sendArpReplyToWaitingRequesters(addr,
89 arpReply.getTargetMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -070090 }
91
92 @Override
93 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -070094 entryAdded(arpReply);
95 }
96
97 @Override
98 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -070099 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
100 }
101 }
102
103 private class ArpCacheEventHandler implements
104 IEventChannelListener<String, ArpCacheNotification> {
105
106 /**
107 * Startup processing.
108 */
109 private void startUp() {
110 //
111 // TODO: Read all state from the database:
112 // For now, as a shortcut we read it from the datagrid
113 //
114 Collection<ArpCacheNotification> arpCacheEvents =
115 arpCacheEventChannel.getAllEntries();
116
117 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
118 entryAdded(arpCacheEvent);
119 }
120 }
121
122 @Override
123 public void entryAdded(ArpCacheNotification value) {
TeruU3c049c42014-04-15 10:13:25 -0700124 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700125 InetAddress targetIpAddress =
126 InetAddress.getByAddress(value.getTargetAddress());
127
128 log.debug("Received entryAdded for ARP cache notification " +
129 "for ip {}, mac {}", targetIpAddress, value.getTargetMacAddress());
130 arpCache.update(targetIpAddress,
131 MACAddress.valueOf(value.getTargetMacAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700132 } catch (UnknownHostException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700133 log.error("Exception: ", e);
TeruU3c049c42014-04-15 10:13:25 -0700134 }
135 }
136
137 @Override
138 public void entryRemoved(ArpCacheNotification value) {
139 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
140 value.getTargetAddress(), value.getTargetMacAddress());
141 try {
142 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
143 } catch (UnknownHostException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700144 log.error("Exception: ", e);
TeruU3c049c42014-04-15 10:13:25 -0700145 }
146 }
147
148 @Override
149 public void entryUpdated(ArpCacheNotification value) {
150 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700151 InetAddress targetIpAddress =
152 InetAddress.getByAddress(value.getTargetAddress());
153
154 log.debug("Received entryUpdated for ARP cache notification " +
155 "for ip {}, mac {}", targetIpAddress, value.getTargetMacAddress());
156 arpCache.update(targetIpAddress,
157 MACAddress.valueOf(value.getTargetMacAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700158 } catch (UnknownHostException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700159 log.error("Exception: ", e);
TeruU3c049c42014-04-15 10:13:25 -0700160 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700161 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700162 }
163
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700164 private static class ArpRequest {
165 private final IArpRequester requester;
166 private final boolean retry;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700167 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200168
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700169 public ArpRequest(IArpRequester requester, boolean retry) {
170 this.requester = requester;
171 this.retry = retry;
Jonathan Hart55316582014-05-09 10:02:58 -0700172
173 requestTime = System.currentTimeMillis();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700174 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200175
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700176 public ArpRequest(ArpRequest old) {
177 this.requester = old.requester;
178 this.retry = old.retry;
179 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200180
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700181 public boolean isExpired() {
Jonathan Hart55316582014-05-09 10:02:58 -0700182 return ((System.currentTimeMillis() - requestTime)
183 > arpRequestTimeoutConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700184 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200185
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700186 public boolean shouldRetry() {
187 return retry;
188 }
189
190 public void dispatchReply(InetAddress ipAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700191 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700192 requester.arpResponse(ipAddress, replyMacAddress);
193 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700194 }
195
196 private class HostArpRequester implements IArpRequester {
197 private final ARP arpRequest;
198 private final long dpid;
199 private final short port;
200
201 public HostArpRequester(ARP arpRequest, long dpid, short port) {
202 this.arpRequest = arpRequest;
203 this.dpid = dpid;
204 this.port = port;
205 }
206
207 @Override
208 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
209 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
210 macAddress);
211 }
212 }
213
214 @Override
215 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
216 Collection<Class<? extends IFloodlightService>> l =
217 new ArrayList<Class<? extends IFloodlightService>>();
218 l.add(IProxyArpService.class);
219 return l;
220 }
221
222 @Override
223 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
224 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
225 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
226 m.put(IProxyArpService.class, this);
227 return m;
228 }
229
230 @Override
231 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
232 Collection<Class<? extends IFloodlightService>> dependencies =
233 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700234 dependencies.add(IRestApiService.class);
235 dependencies.add(IDatagridService.class);
236 dependencies.add(IConfigInfoService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700237 dependencies.add(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700238 dependencies.add(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700239 return dependencies;
240 }
241
242 @Override
243 public void init(FloodlightModuleContext context) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700244 this.configService = context.getServiceImpl(IConfigInfoService.class);
245 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700246 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700247 this.topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700248 this.packetService = context.getServiceImpl(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700249
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700250 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
251 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700252 }
253
254 @Override
255 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700256 Map<String, String> configOptions = context.getConfigParams(this);
257
TeruU8b2d1672014-04-25 17:02:56 -0700258 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700259 arpCleaningTimerPeriodConfig =
260 Long.parseLong(configOptions.get("cleanupmsec"));
TeruU8b2d1672014-04-25 17:02:56 -0700261 } catch (NumberFormatException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700262 log.debug("ArpCleaningTimerPeriod related config options were " +
263 "not set. Using default.");
TeruU8b2d1672014-04-25 17:02:56 -0700264 }
265
Jonathan Hart4aa47532014-04-23 15:23:41 -0700266 Long agingmsec = null;
267 try {
268 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
269 } catch (NumberFormatException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700270 log.debug("ArpEntryTimeout related config options were " +
271 "not set. Using default.");
Jonathan Hart4aa47532014-04-23 15:23:41 -0700272 }
273
274 arpCache = new ArpCache();
275 if (agingmsec != null) {
276 arpCache.setArpEntryTimeoutConfig(agingmsec);
277 }
278
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700279 this.vlan = configService.getVlan();
280 log.info("vlan set to {}", this.vlan);
281
282 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700283 packetService.registerPacketListener(this);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700284 topology = topologyService.getTopology();
Ray Milkey269ffb92014-04-03 14:43:30 -0700285
286 //
287 // Event notification setup: channels and event handlers
288 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700289
290 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
291 arpReplyEventHandler,
292 Long.class,
293 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700294
TeruU3c049c42014-04-15 10:13:25 -0700295 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
296 arpCacheEventHandler,
297 String.class,
298 ArpCacheNotification.class);
299 arpCacheEventHandler.startUp();
300
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700301 Timer arpTimer = new Timer("arp-processing");
302 arpTimer.scheduleAtFixedRate(new TimerTask() {
303 @Override
304 public void run() {
305 doPeriodicArpProcessing();
306 }
TeruU3c049c42014-04-15 10:13:25 -0700307 }, 0, arpTimerPeriodConfig);
308
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700309 Timer arpCacheTimer = new Timer("arp-cleaning");
TeruU3c049c42014-04-15 10:13:25 -0700310 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
311 @Override
312 public void run() {
313 doPeriodicArpCleaning();
314 }
315 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700316 }
317
318 /*
319 * Function that runs periodically to manage the asynchronous request mechanism.
320 * It basically cleans up old ARP requests if we don't get a response for them.
321 * The caller can designate that a request should be retried indefinitely, and
322 * this task will handle that as well.
323 */
324 private void doPeriodicArpProcessing() {
325 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
326 .<InetAddress, ArpRequest>create();
327
Jonathan Hart55316582014-05-09 10:02:58 -0700328 // We must synchronize externally on the Multimap while using an
329 // iterator, even though it's a synchronizedMultimap
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700330 synchronized (arpRequests) {
331 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
332 .entries().iterator();
333
334 while (it.hasNext()) {
335 Map.Entry<InetAddress, ArpRequest> entry = it.next();
336 ArpRequest request = entry.getValue();
337 if (request.isExpired()) {
338 log.debug("Cleaning expired ARP request for {}", entry
339 .getKey().getHostAddress());
340
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700341 it.remove();
342
343 if (request.shouldRetry()) {
344 retryList.put(entry.getKey(), request);
345 }
346 }
347 }
348 }
349
350 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
351 .asMap().entrySet()) {
352
353 InetAddress address = entry.getKey();
354
355 log.debug("Resending ARP request for {}", address.getHostAddress());
356
357 // Only ARP requests sent by the controller will have the retry flag
358 // set, so for now we can just send a new ARP request for that
359 // address.
360 sendArpRequestForAddress(address);
361
362 for (ArpRequest request : entry.getValue()) {
363 arpRequests.put(address, new ArpRequest(request));
364 }
365 }
366 }
367
368 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700369 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruU8b2d1672014-04-25 17:02:56 -0700370
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700371 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
372 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700373 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700374 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700375 handleArpRequest(sw.getDpid(), inPort.getNumber().shortValue(),
376 arp, eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700377 } else if (arp.getOpCode() == ARP.OP_REPLY) {
378 // For replies we simply send a notification via Hazelcast
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700379 sendArpReplyNotification(eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700380 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700381 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700382 }
pingping-linb8757bf2013-12-13 01:48:58 +0800383
TeruU3c049c42014-04-15 10:13:25 -0700384 private void learnArp(ARP arp) {
385 ArpCacheNotification arpCacheNotification = null;
386
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700387 arpCacheNotification = new ArpCacheNotification(
388 arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
TeruU3c049c42014-04-15 10:13:25 -0700389
390 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700391 arpCacheEventChannel.addEntry(InetAddress.getByAddress(
392 arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
TeruU3c049c42014-04-15 10:13:25 -0700393 } catch (UnknownHostException e) {
394 log.error("Exception : ", e);
395 }
396 }
397
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700398 private void handleArpRequest(long dpid, short inPort, ARP arp, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700399 if (log.isTraceEnabled()) {
400 log.trace("ARP request received for {}",
401 inetAddressToString(arp.getTargetProtocolAddress()));
402 }
pingping-linb8757bf2013-12-13 01:48:58 +0800403
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700404 InetAddress target;
405 try {
406 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
407 } catch (UnknownHostException e) {
408 log.debug("Invalid address in ARP request", e);
409 return;
410 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700411
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700412 if (configService.fromExternalNetwork(dpid, inPort)) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700413 // If the request came from outside our network, we only care if
414 // it was a request for one of our interfaces.
415 if (configService.isInterfaceAddress(target)) {
416 log.trace(
417 "ARP request for our interface. Sending reply {} => {}",
418 target.getHostAddress(),
419 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800420
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700421 sendArpReply(arp, dpid, inPort,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700422 configService.getRouterMacAddress());
423 }
pingping-linb8757bf2013-12-13 01:48:58 +0800424
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700425 return;
426 }
pingping-linb8757bf2013-12-13 01:48:58 +0800427
TeruU3c049c42014-04-15 10:13:25 -0700428 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800429
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700430 arpRequests.put(target, new ArpRequest(
431 new HostArpRequester(arp, dpid, inPort), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800432
Jonathan Harte37e4e22014-05-13 19:12:02 -0700433 topology.acquireReadLock();
434 Device targetDevice = topology.getDeviceByMac(
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700435 MACAddress.valueOf(arp.getTargetHardwareAddress()));
Jonathan Harte37e4e22014-05-13 19:12:02 -0700436 topology.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800437
Ray Milkey269ffb92014-04-03 14:43:30 -0700438 if (targetDevice == null) {
439 if (log.isTraceEnabled()) {
440 log.trace("No device info found for {} - broadcasting",
441 target.getHostAddress());
442 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800443
Ray Milkey269ffb92014-04-03 14:43:30 -0700444 // We don't know the device so broadcast the request out
Jonathan Harte3702f22014-04-29 02:56:56 -0700445 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700446 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700447 } else {
448 // Even if the device exists in our database, we do not reply to
449 // the request directly, but check whether the device is still valid
450 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200451
Ray Milkey269ffb92014-04-03 14:43:30 -0700452 if (log.isTraceEnabled()) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700453 log.trace("The target Device Record in DB is: {} => {} " +
454 "from ARP request host at {}/{}", new Object[]{
Ray Milkey269ffb92014-04-03 14:43:30 -0700455 inetAddressToString(arp.getTargetProtocolAddress()),
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700456 macAddress, HexString.toHexString(dpid), inPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700457 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200458
Ray Milkey269ffb92014-04-03 14:43:30 -0700459 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800460
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700461 Iterable<Port> outPorts = targetDevice.getAttachmentPoints();
Ray Milkey269ffb92014-04-03 14:43:30 -0700462
463 if (!outPorts.iterator().hasNext()) {
464 if (log.isTraceEnabled()) {
465 log.trace("Device {} exists but is not connected to any ports" +
466 " - broadcasting", macAddress);
467 }
468
Jonathan Harte3702f22014-04-29 02:56:56 -0700469 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700470 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700471 } else {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700472 for (Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700473
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700474 if (portObject.getOutgoingLink() != null ||
475 portObject.getIncomingLink() != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700476 continue;
477 }
478
479 short outPort = portObject.getNumber().shortValue();
480 Switch outSwitchObject = portObject.getSwitch();
481 long outSwitch = outSwitchObject.getDpid();
482
483 if (log.isTraceEnabled()) {
484 log.trace("Probing device {} on port {}/{}",
485 new Object[]{macAddress,
486 HexString.toHexString(outSwitch), outPort});
487 }
488
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700489 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700490 eth, new SwitchPort(outSwitch, outPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700491 }
492 }
493 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700494 }
pingping-linb8757bf2013-12-13 01:48:58 +0800495
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700496 // TODO this method has not been tested after recent implementation changes.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700497 private void sendArpRequestForAddress(InetAddress ipAddress) {
498 // TODO what should the sender IP address and MAC address be if no
499 // IP addresses are configured? Will there ever be a need to send
500 // ARP requests from the controller in that case?
501 // All-zero MAC address doesn't seem to work - hosts don't respond to it
502
503 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
504 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
505 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
506 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
507 (byte) 0xff, (byte) 0xff, (byte) 0xff};
508
509 ARP arpRequest = new ARP();
510
511 arpRequest
512 .setHardwareType(ARP.HW_TYPE_ETHERNET)
513 .setProtocolType(ARP.PROTO_TYPE_IP)
514 .setHardwareAddressLength(
515 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
516 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
517 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
518 .setTargetProtocolAddress(ipAddress.getAddress());
519
520 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700521 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700522 byte[] senderMacAddress = genericNonZeroMac;
523 if (routerMacAddress != null) {
524 senderMacAddress = routerMacAddress.toBytes();
525 }
526 arpRequest.setSenderHardwareAddress(senderMacAddress);
527
528 byte[] senderIPAddress = zeroIpv4;
529 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700530 if (intf == null) {
531 // TODO handle the case where the controller needs to send an ARP
532 // request but there's not IP configuration. In this case the
533 // request should be broadcast out all edge ports in the network.
534 log.warn("Sending ARP requests with default configuration "
535 + "not supported");
536 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700537 }
538
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700539 senderIPAddress = intf.getIpAddress().getAddress();
540
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700541 arpRequest.setSenderProtocolAddress(senderIPAddress);
542
543 Ethernet eth = new Ethernet();
544 eth.setSourceMACAddress(senderMacAddress)
545 .setDestinationMACAddress(broadcastMac)
546 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
547
548 if (vlan != NO_VLAN) {
549 eth.setVlanID(vlan).setPriorityCode((byte) 0);
550 }
551
552 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700553
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700554 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700555 eth, new SwitchPort(intf.getDpid(), intf.getPort()));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700556 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700557
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700558 // Please leave it for now because this code is needed for SDN-IP.
559 // It will be removed soon.
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700560 /*
TeruU7feef8a2014-04-03 00:15:49 -0700561 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700562 sendArpRequestToSwitches(dstAddress, arpRequest,
563 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700564 }
565
TeruU3c049c42014-04-15 10:13:25 -0700566 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
567 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700568
569 if (configService.hasLayer3Configuration()) {
570 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700571 if (intf != null) {
572 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
573 }
574 else {
575 //TODO here it should be broadcast out all non-interface edge ports.
576 //I think we can assume that if it's not a request for an external
577 //network, it's an ARP for a host in our own network. So we want to
578 //send it out all edge ports that don't have an interface configured
579 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700580 log.debug("No interface found to send ARP request for {}",
581 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700582 }
TeruU3c049c42014-04-15 10:13:25 -0700583 }
584 else {
585 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700586 }
587 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700588 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700589
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700590 private void sendArpReplyNotification(Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700591 ARP arp = (ARP) eth.getPayload();
592
593 if (log.isTraceEnabled()) {
594 log.trace("Sending ARP reply for {} to other ONOS instances",
595 inetAddressToString(arp.getSenderProtocolAddress()));
596 }
597
598 InetAddress targetAddress;
599
600 try {
601 targetAddress = InetAddress.getByAddress(arp
602 .getSenderProtocolAddress());
603 } catch (UnknownHostException e) {
604 log.error("Unknown host", e);
605 return;
606 }
607
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700608 int intAddress = InetAddresses.coerceToInteger(targetAddress);
609
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700610 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
611
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700612 ArpReplyNotification value = new ArpReplyNotification(intAddress, mac);
613
614 log.debug("ArpReplyNotification ip {}, mac {}", intAddress, mac);
TeruU3c049c42014-04-15 10:13:25 -0700615 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700616 }
617
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700618 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700619 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700620 if (log.isTraceEnabled()) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700621 log.trace("Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700622 new Object[]{
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700623 inetAddressToString(arpRequest.getTargetProtocolAddress()),
624 targetMac, inetAddressToString(arpRequest
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700625 .getSenderProtocolAddress())});
626 }
627
628 ARP arpReply = new ARP();
629 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
630 .setProtocolType(ARP.PROTO_TYPE_IP)
631 .setHardwareAddressLength(
632 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
633 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
634 .setOpCode(ARP.OP_REPLY)
635 .setSenderHardwareAddress(targetMac.toBytes())
636 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
637 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
638 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
639
640 Ethernet eth = new Ethernet();
641 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
642 .setSourceMACAddress(targetMac.toBytes())
643 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
644
645 if (vlan != NO_VLAN) {
646 eth.setVlanID(vlan).setPriorityCode((byte) 0);
647 }
648
Jonathan Harte3702f22014-04-29 02:56:56 -0700649 packetService.sendPacket(eth, new SwitchPort(dpid, port));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700650 }
651
652 private String inetAddressToString(byte[] bytes) {
653 try {
654 return InetAddress.getByAddress(bytes).getHostAddress();
655 } catch (UnknownHostException e) {
656 log.debug("Invalid IP address", e);
657 return "";
658 }
659 }
660
661 /*
662 * IProxyArpService methods
663 */
664
665 @Override
666 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700667 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700668 }
669
670 @Override
671 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700672 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700673 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
674
675 // Sanity check to make sure we don't send a request for our own address
676 if (!configService.isInterfaceAddress(ipAddress)) {
677 sendArpRequestForAddress(ipAddress);
678 }
679 }
680
681 @Override
682 public List<String> getMappings() {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700683 return arpCache.getMappings();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700684 }
685
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700686 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700687 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700688 log.debug("Sending ARP reply for {} to requesters",
689 address.getHostAddress());
690
691 // See if anyone's waiting for this ARP reply
692 Set<ArpRequest> requests = arpRequests.get(address);
693
694 // Synchronize on the Multimap while using an iterator for one of the
695 // sets
696 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
697 requests.size());
698 synchronized (arpRequests) {
699 Iterator<ArpRequest> it = requests.iterator();
700 while (it.hasNext()) {
701 ArpRequest request = it.next();
702 it.remove();
703 requestsToSend.add(request);
704 }
705 }
706
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700707 // Don't hold an ARP lock while dispatching requests
708 for (ArpRequest request : requestsToSend) {
709 request.dispatchReply(address, mac);
710 }
711 }
TeruU3c049c42014-04-15 10:13:25 -0700712
713 private void doPeriodicArpCleaning() {
714 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
715 for (InetAddress expireIp : expiredipslist) {
716 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
717 arpCacheEventChannel.removeEntry(expireIp.toString());
718 }
719 }
TeruU8b2d1672014-04-25 17:02:56 -0700720
721 public long getArpEntryTimeout() {
722 return arpCache.getArpEntryTimeout();
723 }
724
725 public long getArpCleaningTimerPeriod() {
726 return arpCleaningTimerPeriodConfig;
727 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700728}