blob: f026745abbb79566e9ed7c1f41d69498f77276c0 [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
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700263 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
264 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700265 }
266
267 @Override
268 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700269 Map<String, String> configOptions = context.getConfigParams(this);
270
TeruU8b2d1672014-04-25 17:02:56 -0700271 try {
272 arpCleaningTimerPeriodConfig = Long.parseLong(configOptions.get("cleanupmsec"));
273 } catch (NumberFormatException e) {
274 log.debug("ArpCleaningTimerPeriod related config options were not set. Use default.");
275 }
276
Jonathan Hart4aa47532014-04-23 15:23:41 -0700277 Long agingmsec = null;
278 try {
279 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
280 } catch (NumberFormatException e) {
281 log.debug("ArpEntryTimeout related config options were not set. Use default.");
282 }
283
284 arpCache = new ArpCache();
285 if (agingmsec != null) {
286 arpCache.setArpEntryTimeoutConfig(agingmsec);
287 }
288
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700289 this.vlan = configService.getVlan();
290 log.info("vlan set to {}", this.vlan);
291
292 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700293 packetService.registerPacketListener(this);
Ray Milkey269ffb92014-04-03 14:43:30 -0700294 networkGraph = networkGraphService.getNetworkGraph();
295
296 //
297 // Event notification setup: channels and event handlers
298 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700299
300 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
301 arpReplyEventHandler,
302 Long.class,
303 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700304
TeruU3c049c42014-04-15 10:13:25 -0700305 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
306 arpCacheEventHandler,
307 String.class,
308 ArpCacheNotification.class);
309 arpCacheEventHandler.startUp();
310
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700311 Timer arpTimer = new Timer("arp-processing");
312 arpTimer.scheduleAtFixedRate(new TimerTask() {
313 @Override
314 public void run() {
315 doPeriodicArpProcessing();
316 }
TeruU3c049c42014-04-15 10:13:25 -0700317 }, 0, arpTimerPeriodConfig);
318
319 Timer arpCacheTimer = new Timer("arp-clearning");
320 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
321 @Override
322 public void run() {
323 doPeriodicArpCleaning();
324 }
325 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700326 }
327
328 /*
329 * Function that runs periodically to manage the asynchronous request mechanism.
330 * It basically cleans up old ARP requests if we don't get a response for them.
331 * The caller can designate that a request should be retried indefinitely, and
332 * this task will handle that as well.
333 */
334 private void doPeriodicArpProcessing() {
335 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
336 .<InetAddress, ArpRequest>create();
337
338 // Have to synchronize externally on the Multimap while using an
339 // iterator,
340 // even though it's a synchronizedMultimap
341 synchronized (arpRequests) {
342 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
343 .entries().iterator();
344
345 while (it.hasNext()) {
346 Map.Entry<InetAddress, ArpRequest> entry = it.next();
347 ArpRequest request = entry.getValue();
348 if (request.isExpired()) {
349 log.debug("Cleaning expired ARP request for {}", entry
350 .getKey().getHostAddress());
351
Ray Milkey269ffb92014-04-03 14:43:30 -0700352 // If the ARP request is expired and then delete the device
Ray Milkey269ffb92014-04-03 14:43:30 -0700353 HostArpRequester requester = (HostArpRequester) request.requester;
354 ARP req = requester.getArpRequest();
TeruU3c049c42014-04-15 10:13:25 -0700355 networkGraph.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700356 Device targetDev = networkGraph.getDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700357 networkGraph.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700358 if (targetDev != null) {
359 onosDeviceService.deleteOnosDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
360 if (log.isDebugEnabled()) {
361 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDev.getMacAddress());
362 }
363 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700364
365 it.remove();
366
367 if (request.shouldRetry()) {
368 retryList.put(entry.getKey(), request);
369 }
370 }
371 }
372 }
373
374 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
375 .asMap().entrySet()) {
376
377 InetAddress address = entry.getKey();
378
379 log.debug("Resending ARP request for {}", address.getHostAddress());
380
381 // Only ARP requests sent by the controller will have the retry flag
382 // set, so for now we can just send a new ARP request for that
383 // address.
384 sendArpRequestForAddress(address);
385
386 for (ArpRequest request : entry.getValue()) {
387 arpRequests.put(address, new ArpRequest(request));
388 }
389 }
390 }
391
392 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700393 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruU8b2d1672014-04-25 17:02:56 -0700394
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700395 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
396 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700397 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700398 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700399 handleArpRequest(sw.getDpid(), inPort.getNumber().shortValue(),
400 arp, eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700401 } else if (arp.getOpCode() == ARP.OP_REPLY) {
402 // For replies we simply send a notification via Hazelcast
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700403 sendArpReplyNotification(eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700404 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700405 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700406 }
pingping-linb8757bf2013-12-13 01:48:58 +0800407
TeruU3c049c42014-04-15 10:13:25 -0700408 private void learnArp(ARP arp) {
409 ArpCacheNotification arpCacheNotification = null;
410
411 arpCacheNotification = new ArpCacheNotification(arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
412
413 try {
414 arpCacheEventChannel.addEntry(InetAddress.getByAddress(arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
415 } catch (UnknownHostException e) {
416 log.error("Exception : ", e);
417 }
418 }
419
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700420 private void handleArpRequest(long dpid, short inPort, ARP arp, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700421 if (log.isTraceEnabled()) {
422 log.trace("ARP request received for {}",
423 inetAddressToString(arp.getTargetProtocolAddress()));
424 }
pingping-linb8757bf2013-12-13 01:48:58 +0800425
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700426 InetAddress target;
427 try {
428 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
429 } catch (UnknownHostException e) {
430 log.debug("Invalid address in ARP request", e);
431 return;
432 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700433
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700434 if (configService.fromExternalNetwork(dpid, inPort)) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700435 // If the request came from outside our network, we only care if
436 // it was a request for one of our interfaces.
437 if (configService.isInterfaceAddress(target)) {
438 log.trace(
439 "ARP request for our interface. Sending reply {} => {}",
440 target.getHostAddress(),
441 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800442
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700443 sendArpReply(arp, dpid, inPort,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700444 configService.getRouterMacAddress());
445 }
pingping-linb8757bf2013-12-13 01:48:58 +0800446
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700447 return;
448 }
pingping-linb8757bf2013-12-13 01:48:58 +0800449
TeruU3c049c42014-04-15 10:13:25 -0700450 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800451
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700452 arpRequests.put(target, new ArpRequest(
453 new HostArpRequester(arp, dpid, inPort), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800454
TeruU3c049c42014-04-15 10:13:25 -0700455 networkGraph.acquireReadLock();
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700456 Device targetDevice = networkGraph.getDeviceByMac(
457 MACAddress.valueOf(arp.getTargetHardwareAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700458 networkGraph.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800459
Ray Milkey269ffb92014-04-03 14:43:30 -0700460 if (targetDevice == null) {
461 if (log.isTraceEnabled()) {
462 log.trace("No device info found for {} - broadcasting",
463 target.getHostAddress());
464 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800465
Ray Milkey269ffb92014-04-03 14:43:30 -0700466 // We don't know the device so broadcast the request out
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700467 packetService.broadcastPacket(eth,
468 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700469 } else {
470 // Even if the device exists in our database, we do not reply to
471 // the request directly, but check whether the device is still valid
472 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200473
Ray Milkey269ffb92014-04-03 14:43:30 -0700474 if (log.isTraceEnabled()) {
475 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
476 new Object[]{
477 inetAddressToString(arp.getTargetProtocolAddress()),
478 macAddress,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700479 HexString.toHexString(dpid), inPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700480 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200481
Ray Milkey269ffb92014-04-03 14:43:30 -0700482 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800483
Ray Milkey269ffb92014-04-03 14:43:30 -0700484 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
485
486 if (!outPorts.iterator().hasNext()) {
487 if (log.isTraceEnabled()) {
488 log.trace("Device {} exists but is not connected to any ports" +
489 " - broadcasting", macAddress);
490 }
491
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700492 packetService.broadcastPacket(eth,
493 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700494 } else {
495 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700496
497 if (portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
498 continue;
499 }
500
501 short outPort = portObject.getNumber().shortValue();
502 Switch outSwitchObject = portObject.getSwitch();
503 long outSwitch = outSwitchObject.getDpid();
504
505 if (log.isTraceEnabled()) {
506 log.trace("Probing device {} on port {}/{}",
507 new Object[]{macAddress,
508 HexString.toHexString(outSwitch), outPort});
509 }
510
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700511 packetService.sendPacket(
512 new SwitchPort(outSwitch, outPort), eth);
Ray Milkey269ffb92014-04-03 14:43:30 -0700513 }
514 }
515 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700516 }
pingping-linb8757bf2013-12-13 01:48:58 +0800517
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700518 // TODO this method has not been tested after recent implementation changes.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700519 private void sendArpRequestForAddress(InetAddress ipAddress) {
520 // TODO what should the sender IP address and MAC address be if no
521 // IP addresses are configured? Will there ever be a need to send
522 // ARP requests from the controller in that case?
523 // All-zero MAC address doesn't seem to work - hosts don't respond to it
524
525 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
526 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
527 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
528 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
529 (byte) 0xff, (byte) 0xff, (byte) 0xff};
530
531 ARP arpRequest = new ARP();
532
533 arpRequest
534 .setHardwareType(ARP.HW_TYPE_ETHERNET)
535 .setProtocolType(ARP.PROTO_TYPE_IP)
536 .setHardwareAddressLength(
537 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
538 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
539 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
540 .setTargetProtocolAddress(ipAddress.getAddress());
541
542 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700543 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700544 byte[] senderMacAddress = genericNonZeroMac;
545 if (routerMacAddress != null) {
546 senderMacAddress = routerMacAddress.toBytes();
547 }
548 arpRequest.setSenderHardwareAddress(senderMacAddress);
549
550 byte[] senderIPAddress = zeroIpv4;
551 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700552 if (intf == null) {
553 // TODO handle the case where the controller needs to send an ARP
554 // request but there's not IP configuration. In this case the
555 // request should be broadcast out all edge ports in the network.
556 log.warn("Sending ARP requests with default configuration "
557 + "not supported");
558 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700559 }
560
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700561 senderIPAddress = intf.getIpAddress().getAddress();
562
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700563 arpRequest.setSenderProtocolAddress(senderIPAddress);
564
565 Ethernet eth = new Ethernet();
566 eth.setSourceMACAddress(senderMacAddress)
567 .setDestinationMACAddress(broadcastMac)
568 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
569
570 if (vlan != NO_VLAN) {
571 eth.setVlanID(vlan).setPriorityCode((byte) 0);
572 }
573
574 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700575
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700576 packetService.sendPacket(
577 new SwitchPort(intf.getDpid(), intf.getPort()), eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700578 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700579
TeruU3c049c42014-04-15 10:13:25 -0700580 //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 -0700581 /*
TeruU7feef8a2014-04-03 00:15:49 -0700582 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700583 sendArpRequestToSwitches(dstAddress, arpRequest,
584 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700585 }
586
TeruU3c049c42014-04-15 10:13:25 -0700587 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
588 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700589
590 if (configService.hasLayer3Configuration()) {
591 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700592 if (intf != null) {
593 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
594 }
595 else {
596 //TODO here it should be broadcast out all non-interface edge ports.
597 //I think we can assume that if it's not a request for an external
598 //network, it's an ARP for a host in our own network. So we want to
599 //send it out all edge ports that don't have an interface configured
600 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700601 log.debug("No interface found to send ARP request for {}",
602 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700603 }
TeruU3c049c42014-04-15 10:13:25 -0700604 }
605 else {
606 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700607 }
608 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700609 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700610
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700611 private void sendArpReplyNotification(Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700612 ARP arp = (ARP) eth.getPayload();
613
614 if (log.isTraceEnabled()) {
615 log.trace("Sending ARP reply for {} to other ONOS instances",
616 inetAddressToString(arp.getSenderProtocolAddress()));
617 }
618
619 InetAddress targetAddress;
620
621 try {
622 targetAddress = InetAddress.getByAddress(arp
623 .getSenderProtocolAddress());
624 } catch (UnknownHostException e) {
625 log.error("Unknown host", e);
626 return;
627 }
628
629 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
630
TeruU3c049c42014-04-15 10:13:25 -0700631 ArpReplyNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700632 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
633 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
TeruU3c049c42014-04-15 10:13:25 -0700634 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700635 }
636
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700637 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700638 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700639 if (log.isTraceEnabled()) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700640 log.trace("Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700641 new Object[]{
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700642 inetAddressToString(arpRequest.getTargetProtocolAddress()),
643 targetMac, inetAddressToString(arpRequest
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700644 .getSenderProtocolAddress())});
645 }
646
647 ARP arpReply = new ARP();
648 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
649 .setProtocolType(ARP.PROTO_TYPE_IP)
650 .setHardwareAddressLength(
651 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
652 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
653 .setOpCode(ARP.OP_REPLY)
654 .setSenderHardwareAddress(targetMac.toBytes())
655 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
656 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
657 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
658
659 Ethernet eth = new Ethernet();
660 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
661 .setSourceMACAddress(targetMac.toBytes())
662 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
663
664 if (vlan != NO_VLAN) {
665 eth.setVlanID(vlan).setPriorityCode((byte) 0);
666 }
667
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700668 packetService.sendPacket(new SwitchPort(dpid, port), eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700669 }
670
671 private String inetAddressToString(byte[] bytes) {
672 try {
673 return InetAddress.getByAddress(bytes).getHostAddress();
674 } catch (UnknownHostException e) {
675 log.debug("Invalid IP address", e);
676 return "";
677 }
678 }
679
680 /*
681 * IProxyArpService methods
682 */
683
684 @Override
685 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700686 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700687 }
688
689 @Override
690 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700691 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700692 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
693
694 // Sanity check to make sure we don't send a request for our own address
695 if (!configService.isInterfaceAddress(ipAddress)) {
696 sendArpRequestForAddress(ipAddress);
697 }
698 }
699
700 @Override
701 public List<String> getMappings() {
TeruU8b2d1672014-04-25 17:02:56 -0700702 return arpCache.getMappings();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700703 }
704
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700705 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700706 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700707 log.debug("Sending ARP reply for {} to requesters",
708 address.getHostAddress());
709
710 // See if anyone's waiting for this ARP reply
711 Set<ArpRequest> requests = arpRequests.get(address);
712
713 // Synchronize on the Multimap while using an iterator for one of the
714 // sets
715 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
716 requests.size());
717 synchronized (arpRequests) {
718 Iterator<ArpRequest> it = requests.iterator();
719 while (it.hasNext()) {
720 ArpRequest request = it.next();
721 it.remove();
722 requestsToSend.add(request);
723 }
724 }
725
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700726 // Don't hold an ARP lock while dispatching requests
727 for (ArpRequest request : requestsToSend) {
728 request.dispatchReply(address, mac);
729 }
730 }
TeruU3c049c42014-04-15 10:13:25 -0700731
732 private void doPeriodicArpCleaning() {
733 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
734 for (InetAddress expireIp : expiredipslist) {
735 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
736 arpCacheEventChannel.removeEntry(expireIp.toString());
737 }
738 }
TeruU8b2d1672014-04-25 17:02:56 -0700739
740 public long getArpEntryTimeout() {
741 return arpCache.getArpEntryTimeout();
742 }
743
744 public long getArpCleaningTimerPeriod() {
745 return arpCleaningTimerPeriodConfig;
746 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700747}