blob: c1f80d87cc1c20e71b7124738f6046a55b9462a1 [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;
TeruU7feef8a2014-04-03 00:15:49 -07005import java.nio.ByteBuffer;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12006import java.util.ArrayList;
7import java.util.Collection;
Jonathan Harte93aed42013-12-05 18:39:50 -08008import java.util.HashMap;
Jonathan Hart6261dcd2013-07-22 17:58:35 +12009import java.util.Iterator;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120010import java.util.List;
11import java.util.Map;
12import java.util.Set;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120013import java.util.Timer;
14import java.util.TimerTask;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120015
Jonathan Harte93aed42013-12-05 18:39:50 -080016import net.floodlightcontroller.core.module.FloodlightModuleContext;
17import net.floodlightcontroller.core.module.IFloodlightModule;
18import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart5afde492013-10-01 12:30:53 +130019import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120020import net.floodlightcontroller.util.MACAddress;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070021import net.onrc.onos.api.packet.IPacketListener;
22import net.onrc.onos.api.packet.IPacketService;
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 Hart23701d12014-04-03 10:45:48 -070027import net.onrc.onos.core.devicemanager.IOnosDeviceService;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070028import net.onrc.onos.core.main.config.IConfigInfoService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070029import net.onrc.onos.core.packet.ARP;
30import net.onrc.onos.core.packet.Ethernet;
31import net.onrc.onos.core.packet.IPv4;
Jonathan Hart472062d2014-04-03 10:56:48 -070032import net.onrc.onos.core.topology.Device;
33import net.onrc.onos.core.topology.INetworkGraphService;
34import net.onrc.onos.core.topology.NetworkGraph;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070035import net.onrc.onos.core.topology.Port;
Jonathan Hart472062d2014-04-03 10:56:48 -070036import net.onrc.onos.core.topology.Switch;
Jonathan Hart23701d12014-04-03 10:45:48 -070037import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120038
Jonathan Hart8ec133c2013-06-26 15:25:18 +120039import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Jonathan Hart4dfc3652013-08-02 20:22:36 +120043import com.google.common.collect.HashMultimap;
44import com.google.common.collect.Multimaps;
45import com.google.common.collect.SetMultimap;
46
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
TeruU3c049c42014-04-15 10:13:25 -070052 private static long arpTimerPeriodConfig = 100; // ms
53 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
67 private INetworkGraphService networkGraphService;
68 private NetworkGraph networkGraph;
69 private IOnosDeviceService onosDeviceService;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070070 private IPacketService packetService;
Jonathan Harte93aed42013-12-05 18:39:50 -080071
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070072 private short vlan;
73 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080074
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070075 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120076
TeruU3c049c42014-04-15 10:13:25 -070077 private ArpCache arpCache;
78
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070079 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070080 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070081
Ray Milkey269ffb92014-04-03 14:43:30 -070082 @Override
83 public void entryAdded(ArpReplyNotification arpReply) {
84 log.debug("Received ARP reply notification for ip {}, mac {}",
85 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
TeruU3c049c42014-04-15 10:13:25 -070086 //This 4 means ipv4 addr size. Need to change it in the future.
Ray Milkey269ffb92014-04-03 14:43:30 -070087 ByteBuffer buffer = ByteBuffer.allocate(4);
88 buffer.putInt(arpReply.getTargetAddress());
89 InetAddress addr = null;
90 try {
91 addr = InetAddress.getByAddress(buffer.array());
92 } catch (UnknownHostException e) {
93 log.error("Exception:", e);
94 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070095
Ray Milkey269ffb92014-04-03 14:43:30 -070096 if (addr != null) {
97 sendArpReplyToWaitingRequesters(addr,
98 arpReply.getTargetMacAddress());
99 }
100 }
101
102 @Override
103 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700104 entryAdded(arpReply);
105 }
106
107 @Override
108 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -0700109 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
110 }
111 }
112
113 private class ArpCacheEventHandler implements
114 IEventChannelListener<String, ArpCacheNotification> {
115
116 /**
117 * Startup processing.
118 */
119 private void startUp() {
120 //
121 // TODO: Read all state from the database:
122 // For now, as a shortcut we read it from the datagrid
123 //
124 Collection<ArpCacheNotification> arpCacheEvents =
125 arpCacheEventChannel.getAllEntries();
126
127 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
128 entryAdded(arpCacheEvent);
129 }
130 }
131
132 @Override
133 public void entryAdded(ArpCacheNotification value) {
134
135 try {
136 log.debug("Received entryAdded for ARP cache notification for ip {}, mac {}",
137 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
138 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
139 } catch (UnknownHostException e) {
140 log.error("Exception : ", e);
141 }
142 }
143
144 @Override
145 public void entryRemoved(ArpCacheNotification value) {
146 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
147 value.getTargetAddress(), value.getTargetMacAddress());
148 try {
149 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
150 } catch (UnknownHostException e) {
151 log.error("Exception : ", e);
152 }
153 }
154
155 @Override
156 public void entryUpdated(ArpCacheNotification value) {
157 try {
158 log.debug("Received entryUpdated for ARP cache notification for ip {}, mac {}",
159 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
160 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
161 } catch (UnknownHostException e) {
162 log.error("Exception : ", e);
163 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700164 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700165 }
166
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700167 private static class ArpRequest {
168 private final IArpRequester requester;
169 private final boolean retry;
170 private boolean sent = false;
171 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200172
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700173 public ArpRequest(IArpRequester requester, boolean retry) {
174 this.requester = requester;
175 this.retry = retry;
176 }
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() {
184 return sent
TeruU3c049c42014-04-15 10:13:25 -0700185 && ((System.currentTimeMillis() - requestTime) > 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 }
196
197 public void setRequestTime() {
198 this.requestTime = System.currentTimeMillis();
199 this.sent = true;
200 }
201 }
202
203 private class HostArpRequester implements IArpRequester {
204 private final ARP arpRequest;
205 private final long dpid;
206 private final short port;
207
208 public HostArpRequester(ARP arpRequest, long dpid, short port) {
209 this.arpRequest = arpRequest;
210 this.dpid = dpid;
211 this.port = port;
212 }
213
214 @Override
215 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
216 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
217 macAddress);
218 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700219
220 public ARP getArpRequest() {
221 return arpRequest;
222 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700223 }
224
225 @Override
226 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
227 Collection<Class<? extends IFloodlightService>> l =
228 new ArrayList<Class<? extends IFloodlightService>>();
229 l.add(IProxyArpService.class);
230 return l;
231 }
232
233 @Override
234 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
235 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
236 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
237 m.put(IProxyArpService.class, this);
238 return m;
239 }
240
241 @Override
242 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
243 Collection<Class<? extends IFloodlightService>> dependencies =
244 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700245 dependencies.add(IRestApiService.class);
246 dependencies.add(IDatagridService.class);
247 dependencies.add(IConfigInfoService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700248 dependencies.add(INetworkGraphService.class);
249 dependencies.add(IOnosDeviceService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700250 dependencies.add(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700251 return dependencies;
252 }
253
254 @Override
255 public void init(FloodlightModuleContext context) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700256 this.configService = context.getServiceImpl(IConfigInfoService.class);
257 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700258 this.datagrid = context.getServiceImpl(IDatagridService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700259 this.networkGraphService = context.getServiceImpl(INetworkGraphService.class);
260 this.onosDeviceService = context.getServiceImpl(IOnosDeviceService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700261 this.packetService = context.getServiceImpl(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700262
TeruU3c049c42014-04-15 10:13:25 -0700263 Map<String, String> configOptions = context.getConfigParams(this);
TeruU3c049c42014-04-15 10:13:25 -0700264
265 try {
266 arpCleaningTimerPeriodConfig = Long.parseLong(configOptions.get("cleanupmsec"));
267 } catch (NumberFormatException e) {
268 log.debug("ArpCleaningTimerPeriod related config options were not set. Use default.");
269 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700270
271 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
272 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700273 }
274
275 @Override
276 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700277 Map<String, String> configOptions = context.getConfigParams(this);
278
279 Long agingmsec = null;
280 try {
281 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
282 } catch (NumberFormatException e) {
283 log.debug("ArpEntryTimeout related config options were not set. Use default.");
284 }
285
286 arpCache = new ArpCache();
287 if (agingmsec != null) {
288 arpCache.setArpEntryTimeoutConfig(agingmsec);
289 }
290
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700291 this.vlan = configService.getVlan();
292 log.info("vlan set to {}", this.vlan);
293
294 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700295 packetService.registerPacketListener(this);
Ray Milkey269ffb92014-04-03 14:43:30 -0700296 networkGraph = networkGraphService.getNetworkGraph();
297
298 //
299 // Event notification setup: channels and event handlers
300 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700301
302 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
303 arpReplyEventHandler,
304 Long.class,
305 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700306
TeruU3c049c42014-04-15 10:13:25 -0700307 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
308 arpCacheEventHandler,
309 String.class,
310 ArpCacheNotification.class);
311 arpCacheEventHandler.startUp();
312
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700313 Timer arpTimer = new Timer("arp-processing");
314 arpTimer.scheduleAtFixedRate(new TimerTask() {
315 @Override
316 public void run() {
317 doPeriodicArpProcessing();
318 }
TeruU3c049c42014-04-15 10:13:25 -0700319 }, 0, arpTimerPeriodConfig);
320
321 Timer arpCacheTimer = new Timer("arp-clearning");
322 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
323 @Override
324 public void run() {
325 doPeriodicArpCleaning();
326 }
327 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700328 }
329
330 /*
331 * Function that runs periodically to manage the asynchronous request mechanism.
332 * It basically cleans up old ARP requests if we don't get a response for them.
333 * The caller can designate that a request should be retried indefinitely, and
334 * this task will handle that as well.
335 */
336 private void doPeriodicArpProcessing() {
337 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
338 .<InetAddress, ArpRequest>create();
339
340 // Have to synchronize externally on the Multimap while using an
341 // iterator,
342 // even though it's a synchronizedMultimap
343 synchronized (arpRequests) {
344 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
345 .entries().iterator();
346
347 while (it.hasNext()) {
348 Map.Entry<InetAddress, ArpRequest> entry = it.next();
349 ArpRequest request = entry.getValue();
350 if (request.isExpired()) {
351 log.debug("Cleaning expired ARP request for {}", entry
352 .getKey().getHostAddress());
353
Ray Milkey269ffb92014-04-03 14:43:30 -0700354 // If the ARP request is expired and then delete the device
Ray Milkey269ffb92014-04-03 14:43:30 -0700355 HostArpRequester requester = (HostArpRequester) request.requester;
356 ARP req = requester.getArpRequest();
TeruU3c049c42014-04-15 10:13:25 -0700357 networkGraph.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700358 Device targetDev = networkGraph.getDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700359 networkGraph.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700360 if (targetDev != null) {
361 onosDeviceService.deleteOnosDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
362 if (log.isDebugEnabled()) {
363 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDev.getMacAddress());
364 }
365 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700366
367 it.remove();
368
369 if (request.shouldRetry()) {
370 retryList.put(entry.getKey(), request);
371 }
372 }
373 }
374 }
375
376 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
377 .asMap().entrySet()) {
378
379 InetAddress address = entry.getKey();
380
381 log.debug("Resending ARP request for {}", address.getHostAddress());
382
383 // Only ARP requests sent by the controller will have the retry flag
384 // set, so for now we can just send a new ARP request for that
385 // address.
386 sendArpRequestForAddress(address);
387
388 for (ArpRequest request : entry.getValue()) {
389 arpRequests.put(address, new ArpRequest(request));
390 }
391 }
392 }
393
394 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700395 public void receive(Switch sw, Port inPort, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700396 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
397 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700398 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700399 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700400 handleArpRequest(sw.getDpid(), inPort.getNumber().shortValue(),
401 arp, eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700402 } else if (arp.getOpCode() == ARP.OP_REPLY) {
403 // For replies we simply send a notification via Hazelcast
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700404 sendArpReplyNotification(eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700405 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700406 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700407 }
pingping-linb8757bf2013-12-13 01:48:58 +0800408
TeruU3c049c42014-04-15 10:13:25 -0700409 private void learnArp(ARP arp) {
410 ArpCacheNotification arpCacheNotification = null;
411
412 arpCacheNotification = new ArpCacheNotification(arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
413
414 try {
415 arpCacheEventChannel.addEntry(InetAddress.getByAddress(arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
416 } catch (UnknownHostException e) {
417 log.error("Exception : ", e);
418 }
419 }
420
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700421 private void handleArpRequest(long dpid, short inPort, ARP arp, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700422 if (log.isTraceEnabled()) {
423 log.trace("ARP request received for {}",
424 inetAddressToString(arp.getTargetProtocolAddress()));
425 }
pingping-linb8757bf2013-12-13 01:48:58 +0800426
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700427 InetAddress target;
428 try {
429 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
430 } catch (UnknownHostException e) {
431 log.debug("Invalid address in ARP request", e);
432 return;
433 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700434
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700435 if (configService.fromExternalNetwork(dpid, inPort)) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700436 // If the request came from outside our network, we only care if
437 // it was a request for one of our interfaces.
438 if (configService.isInterfaceAddress(target)) {
439 log.trace(
440 "ARP request for our interface. Sending reply {} => {}",
441 target.getHostAddress(),
442 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800443
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700444 sendArpReply(arp, dpid, inPort,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700445 configService.getRouterMacAddress());
446 }
pingping-linb8757bf2013-12-13 01:48:58 +0800447
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700448 return;
449 }
pingping-linb8757bf2013-12-13 01:48:58 +0800450
TeruU3c049c42014-04-15 10:13:25 -0700451 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800452
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700453 arpRequests.put(target, new ArpRequest(
454 new HostArpRequester(arp, dpid, inPort), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800455
TeruU3c049c42014-04-15 10:13:25 -0700456 networkGraph.acquireReadLock();
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700457 Device targetDevice = networkGraph.getDeviceByMac(
458 MACAddress.valueOf(arp.getTargetHardwareAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700459 networkGraph.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800460
Ray Milkey269ffb92014-04-03 14:43:30 -0700461 if (targetDevice == null) {
462 if (log.isTraceEnabled()) {
463 log.trace("No device info found for {} - broadcasting",
464 target.getHostAddress());
465 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800466
Ray Milkey269ffb92014-04-03 14:43:30 -0700467 // We don't know the device so broadcast the request out
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700468 packetService.broadcastPacket(eth,
469 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700470 } else {
471 // Even if the device exists in our database, we do not reply to
472 // the request directly, but check whether the device is still valid
473 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200474
Ray Milkey269ffb92014-04-03 14:43:30 -0700475 if (log.isTraceEnabled()) {
476 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
477 new Object[]{
478 inetAddressToString(arp.getTargetProtocolAddress()),
479 macAddress,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700480 HexString.toHexString(dpid), inPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700481 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200482
Ray Milkey269ffb92014-04-03 14:43:30 -0700483 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800484
Ray Milkey269ffb92014-04-03 14:43:30 -0700485 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
486
487 if (!outPorts.iterator().hasNext()) {
488 if (log.isTraceEnabled()) {
489 log.trace("Device {} exists but is not connected to any ports" +
490 " - broadcasting", macAddress);
491 }
492
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700493 packetService.broadcastPacket(eth,
494 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700495 } else {
496 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700497
498 if (portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
499 continue;
500 }
501
502 short outPort = portObject.getNumber().shortValue();
503 Switch outSwitchObject = portObject.getSwitch();
504 long outSwitch = outSwitchObject.getDpid();
505
506 if (log.isTraceEnabled()) {
507 log.trace("Probing device {} on port {}/{}",
508 new Object[]{macAddress,
509 HexString.toHexString(outSwitch), outPort});
510 }
511
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700512 packetService.sendPacket(
513 new SwitchPort(outSwitch, outPort), eth);
Ray Milkey269ffb92014-04-03 14:43:30 -0700514 }
515 }
516 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700517 }
pingping-linb8757bf2013-12-13 01:48:58 +0800518
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700519 // TODO this method has not been tested after recent implementation changes.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700520 private void sendArpRequestForAddress(InetAddress ipAddress) {
521 // TODO what should the sender IP address and MAC address be if no
522 // IP addresses are configured? Will there ever be a need to send
523 // ARP requests from the controller in that case?
524 // All-zero MAC address doesn't seem to work - hosts don't respond to it
525
526 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
527 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
528 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
529 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
530 (byte) 0xff, (byte) 0xff, (byte) 0xff};
531
532 ARP arpRequest = new ARP();
533
534 arpRequest
535 .setHardwareType(ARP.HW_TYPE_ETHERNET)
536 .setProtocolType(ARP.PROTO_TYPE_IP)
537 .setHardwareAddressLength(
538 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
539 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
540 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
541 .setTargetProtocolAddress(ipAddress.getAddress());
542
543 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700544 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700545 byte[] senderMacAddress = genericNonZeroMac;
546 if (routerMacAddress != null) {
547 senderMacAddress = routerMacAddress.toBytes();
548 }
549 arpRequest.setSenderHardwareAddress(senderMacAddress);
550
551 byte[] senderIPAddress = zeroIpv4;
552 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700553 if (intf == null) {
554 // TODO handle the case where the controller needs to send an ARP
555 // request but there's not IP configuration. In this case the
556 // request should be broadcast out all edge ports in the network.
557 log.warn("Sending ARP requests with default configuration "
558 + "not supported");
559 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700560 }
561
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700562 senderIPAddress = intf.getIpAddress().getAddress();
563
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700564 arpRequest.setSenderProtocolAddress(senderIPAddress);
565
566 Ethernet eth = new Ethernet();
567 eth.setSourceMACAddress(senderMacAddress)
568 .setDestinationMACAddress(broadcastMac)
569 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
570
571 if (vlan != NO_VLAN) {
572 eth.setVlanID(vlan).setPriorityCode((byte) 0);
573 }
574
575 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700576
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700577 packetService.sendPacket(
578 new SwitchPort(intf.getDpid(), intf.getPort()), eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700579 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700580
TeruU3c049c42014-04-15 10:13:25 -0700581 //Please leave it for now because this code is needed for SDN-IP. It will be removed soon.
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700582 /*
TeruU7feef8a2014-04-03 00:15:49 -0700583 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700584 sendArpRequestToSwitches(dstAddress, arpRequest,
585 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700586 }
587
TeruU3c049c42014-04-15 10:13:25 -0700588 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
589 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700590
591 if (configService.hasLayer3Configuration()) {
592 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700593 if (intf != null) {
594 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
595 }
596 else {
597 //TODO here it should be broadcast out all non-interface edge ports.
598 //I think we can assume that if it's not a request for an external
599 //network, it's an ARP for a host in our own network. So we want to
600 //send it out all edge ports that don't have an interface configured
601 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700602 log.debug("No interface found to send ARP request for {}",
603 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700604 }
TeruU3c049c42014-04-15 10:13:25 -0700605 }
606 else {
607 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700608 }
609 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700610 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700611
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700612 private void sendArpReplyNotification(Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700613 ARP arp = (ARP) eth.getPayload();
614
615 if (log.isTraceEnabled()) {
616 log.trace("Sending ARP reply for {} to other ONOS instances",
617 inetAddressToString(arp.getSenderProtocolAddress()));
618 }
619
620 InetAddress targetAddress;
621
622 try {
623 targetAddress = InetAddress.getByAddress(arp
624 .getSenderProtocolAddress());
625 } catch (UnknownHostException e) {
626 log.error("Unknown host", e);
627 return;
628 }
629
630 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
631
TeruU3c049c42014-04-15 10:13:25 -0700632 ArpReplyNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700633 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
634 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
TeruU3c049c42014-04-15 10:13:25 -0700635 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700636 }
637
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700638 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700639 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700640 if (log.isTraceEnabled()) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700641 log.trace("Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700642 new Object[]{
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700643 inetAddressToString(arpRequest.getTargetProtocolAddress()),
644 targetMac, inetAddressToString(arpRequest
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700645 .getSenderProtocolAddress())});
646 }
647
648 ARP arpReply = new ARP();
649 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
650 .setProtocolType(ARP.PROTO_TYPE_IP)
651 .setHardwareAddressLength(
652 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
653 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
654 .setOpCode(ARP.OP_REPLY)
655 .setSenderHardwareAddress(targetMac.toBytes())
656 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
657 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
658 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
659
660 Ethernet eth = new Ethernet();
661 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
662 .setSourceMACAddress(targetMac.toBytes())
663 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
664
665 if (vlan != NO_VLAN) {
666 eth.setVlanID(vlan).setPriorityCode((byte) 0);
667 }
668
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700669 packetService.sendPacket(new SwitchPort(dpid, port), eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700670 }
671
672 private String inetAddressToString(byte[] bytes) {
673 try {
674 return InetAddress.getByAddress(bytes).getHostAddress();
675 } catch (UnknownHostException e) {
676 log.debug("Invalid IP address", e);
677 return "";
678 }
679 }
680
681 /*
682 * IProxyArpService methods
683 */
684
685 @Override
686 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700687 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700688 }
689
690 @Override
691 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700692 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700693 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
694
695 // Sanity check to make sure we don't send a request for our own address
696 if (!configService.isInterfaceAddress(ipAddress)) {
697 sendArpRequestForAddress(ipAddress);
698 }
699 }
700
701 @Override
702 public List<String> getMappings() {
703 return new ArrayList<String>();
704 }
705
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700706 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700707 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700708 log.debug("Sending ARP reply for {} to requesters",
709 address.getHostAddress());
710
711 // See if anyone's waiting for this ARP reply
712 Set<ArpRequest> requests = arpRequests.get(address);
713
714 // Synchronize on the Multimap while using an iterator for one of the
715 // sets
716 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
717 requests.size());
718 synchronized (arpRequests) {
719 Iterator<ArpRequest> it = requests.iterator();
720 while (it.hasNext()) {
721 ArpRequest request = it.next();
722 it.remove();
723 requestsToSend.add(request);
724 }
725 }
726
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700727 // Don't hold an ARP lock while dispatching requests
728 for (ArpRequest request : requestsToSend) {
729 request.dispatchReply(address, mac);
730 }
731 }
TeruU3c049c42014-04-15 10:13:25 -0700732
733 private void doPeriodicArpCleaning() {
734 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
735 for (InetAddress expireIp : expiredipslist) {
736 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
737 arpCacheEventChannel.removeEntry(expireIp.toString());
738 }
739 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700740}