blob: 90ec66263190e72ba9ef5993322c1ac15ea4af3d [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;
Yuta HIGUCHI238fa2a2014-05-01 09:56:46 -070023import net.onrc.onos.apps.proxyarp.web.ArpWebRoutable;
Jonathan Hart8f6dc092014-04-18 15:56:43 -070024import net.onrc.onos.apps.sdnip.Interface;
Jonathan Hart6df90172014-04-03 10:13:11 -070025import net.onrc.onos.core.datagrid.IDatagridService;
26import net.onrc.onos.core.datagrid.IEventChannel;
27import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hart23701d12014-04-03 10:45:48 -070028import net.onrc.onos.core.devicemanager.IOnosDeviceService;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070029import net.onrc.onos.core.main.config.IConfigInfoService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070030import net.onrc.onos.core.packet.ARP;
31import net.onrc.onos.core.packet.Ethernet;
32import net.onrc.onos.core.packet.IPv4;
Jonathan Hart472062d2014-04-03 10:56:48 -070033import net.onrc.onos.core.topology.Device;
Jonathan Harte37e4e22014-05-13 19:12:02 -070034import net.onrc.onos.core.topology.ITopologyService;
35import net.onrc.onos.core.topology.Topology;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070036import net.onrc.onos.core.topology.Port;
Jonathan Hart472062d2014-04-03 10:56:48 -070037import net.onrc.onos.core.topology.Switch;
Jonathan Hart23701d12014-04-03 10:45:48 -070038import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120039
Jonathan Hart8ec133c2013-06-26 15:25:18 +120040import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
Jonathan Hart4dfc3652013-08-02 20:22:36 +120044import com.google.common.collect.HashMultimap;
45import com.google.common.collect.Multimaps;
46import com.google.common.collect.SetMultimap;
47
Jonathan Hart51dc5e12014-04-22 11:03:59 -070048public class ProxyArpManager implements IProxyArpService, IFloodlightModule,
49 IPacketListener {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070050 private static final Logger log = LoggerFactory
51 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080052
TeruU3c049c42014-04-15 10:13:25 -070053 private static long arpTimerPeriodConfig = 100; // ms
54 private static int arpRequestTimeoutConfig = 2000; // ms
55 private long arpCleaningTimerPeriodConfig = 60 * 1000; // ms (1 min)
Jonathan Hartda4d0e12013-09-30 21:00:20 +130056
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070057 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070058 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
TeruU3c049c42014-04-15 10:13:25 -070059 private IEventChannel<String, ArpCacheNotification> arpCacheEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070060 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU3c049c42014-04-15 10:13:25 -070061 private static final String ARP_CACHE_CHANNEL_NAME = "onos.arp_cache";
62 private final ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
TeruU3c049c42014-04-15 10:13:25 -070063 private final ArpCacheEventHandler arpCacheEventHandler = new ArpCacheEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070064
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070065 private IConfigInfoService configService;
66 private IRestApiService restApi;
Ray Milkey269ffb92014-04-03 14:43:30 -070067
Jonathan Harte37e4e22014-05-13 19:12:02 -070068 private ITopologyService topologyService;
69 private Topology topology;
Ray Milkey269ffb92014-04-03 14:43:30 -070070 private IOnosDeviceService onosDeviceService;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070071 private IPacketService packetService;
Jonathan Harte93aed42013-12-05 18:39:50 -080072
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070073 private short vlan;
74 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080075
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070076 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120077
TeruU3c049c42014-04-15 10:13:25 -070078 private ArpCache arpCache;
79
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070080 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070081 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070082
Ray Milkey269ffb92014-04-03 14:43:30 -070083 @Override
84 public void entryAdded(ArpReplyNotification arpReply) {
85 log.debug("Received ARP reply notification for ip {}, mac {}",
86 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
TeruU3c049c42014-04-15 10:13:25 -070087 //This 4 means ipv4 addr size. Need to change it in the future.
Ray Milkey269ffb92014-04-03 14:43:30 -070088 ByteBuffer buffer = ByteBuffer.allocate(4);
89 buffer.putInt(arpReply.getTargetAddress());
90 InetAddress addr = null;
91 try {
92 addr = InetAddress.getByAddress(buffer.array());
93 } catch (UnknownHostException e) {
94 log.error("Exception:", e);
95 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070096
Ray Milkey269ffb92014-04-03 14:43:30 -070097 if (addr != null) {
98 sendArpReplyToWaitingRequesters(addr,
99 arpReply.getTargetMacAddress());
100 }
101 }
102
103 @Override
104 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700105 entryAdded(arpReply);
106 }
107
108 @Override
109 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -0700110 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
111 }
112 }
113
114 private class ArpCacheEventHandler implements
115 IEventChannelListener<String, ArpCacheNotification> {
116
117 /**
118 * Startup processing.
119 */
120 private void startUp() {
121 //
122 // TODO: Read all state from the database:
123 // For now, as a shortcut we read it from the datagrid
124 //
125 Collection<ArpCacheNotification> arpCacheEvents =
126 arpCacheEventChannel.getAllEntries();
127
128 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
129 entryAdded(arpCacheEvent);
130 }
131 }
132
133 @Override
134 public void entryAdded(ArpCacheNotification value) {
135
136 try {
137 log.debug("Received entryAdded for ARP cache notification for ip {}, mac {}",
138 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
139 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
140 } catch (UnknownHostException e) {
141 log.error("Exception : ", e);
142 }
143 }
144
145 @Override
146 public void entryRemoved(ArpCacheNotification value) {
147 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
148 value.getTargetAddress(), value.getTargetMacAddress());
149 try {
150 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
151 } catch (UnknownHostException e) {
152 log.error("Exception : ", e);
153 }
154 }
155
156 @Override
157 public void entryUpdated(ArpCacheNotification value) {
158 try {
159 log.debug("Received entryUpdated for ARP cache notification for ip {}, mac {}",
160 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
161 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
162 } catch (UnknownHostException e) {
163 log.error("Exception : ", e);
164 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700165 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700166 }
167
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700168 private static class ArpRequest {
169 private final IArpRequester requester;
170 private final boolean retry;
171 private boolean sent = false;
172 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200173
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700174 public ArpRequest(IArpRequester requester, boolean retry) {
175 this.requester = requester;
176 this.retry = retry;
177 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200178
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700179 public ArpRequest(ArpRequest old) {
180 this.requester = old.requester;
181 this.retry = old.retry;
182 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200183
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700184 public boolean isExpired() {
185 return sent
TeruU3c049c42014-04-15 10:13:25 -0700186 && ((System.currentTimeMillis() - requestTime) > arpRequestTimeoutConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700187 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200188
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700189 public boolean shouldRetry() {
190 return retry;
191 }
192
193 public void dispatchReply(InetAddress ipAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700194 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700195 requester.arpResponse(ipAddress, replyMacAddress);
196 }
197
198 public void setRequestTime() {
199 this.requestTime = System.currentTimeMillis();
200 this.sent = true;
201 }
202 }
203
204 private class HostArpRequester implements IArpRequester {
205 private final ARP arpRequest;
206 private final long dpid;
207 private final short port;
208
209 public HostArpRequester(ARP arpRequest, long dpid, short port) {
210 this.arpRequest = arpRequest;
211 this.dpid = dpid;
212 this.port = port;
213 }
214
215 @Override
216 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
217 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
218 macAddress);
219 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700220
221 public ARP getArpRequest() {
222 return arpRequest;
223 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700224 }
225
226 @Override
227 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
228 Collection<Class<? extends IFloodlightService>> l =
229 new ArrayList<Class<? extends IFloodlightService>>();
230 l.add(IProxyArpService.class);
231 return l;
232 }
233
234 @Override
235 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
236 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
237 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
238 m.put(IProxyArpService.class, this);
239 return m;
240 }
241
242 @Override
243 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
244 Collection<Class<? extends IFloodlightService>> dependencies =
245 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700246 dependencies.add(IRestApiService.class);
247 dependencies.add(IDatagridService.class);
248 dependencies.add(IConfigInfoService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700249 dependencies.add(ITopologyService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700250 dependencies.add(IOnosDeviceService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700251 dependencies.add(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700252 return dependencies;
253 }
254
255 @Override
256 public void init(FloodlightModuleContext context) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700257 this.configService = context.getServiceImpl(IConfigInfoService.class);
258 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700259 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700260 this.topologyService = context.getServiceImpl(ITopologyService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700261 this.onosDeviceService = context.getServiceImpl(IOnosDeviceService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700262 this.packetService = context.getServiceImpl(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700263
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700264 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
265 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700266 }
267
268 @Override
269 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700270 Map<String, String> configOptions = context.getConfigParams(this);
271
TeruU8b2d1672014-04-25 17:02:56 -0700272 try {
273 arpCleaningTimerPeriodConfig = Long.parseLong(configOptions.get("cleanupmsec"));
274 } catch (NumberFormatException e) {
275 log.debug("ArpCleaningTimerPeriod related config options were not set. Use default.");
276 }
277
Jonathan Hart4aa47532014-04-23 15:23:41 -0700278 Long agingmsec = null;
279 try {
280 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
281 } catch (NumberFormatException e) {
282 log.debug("ArpEntryTimeout related config options were not set. Use default.");
283 }
284
285 arpCache = new ArpCache();
286 if (agingmsec != null) {
287 arpCache.setArpEntryTimeoutConfig(agingmsec);
288 }
289
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700290 this.vlan = configService.getVlan();
291 log.info("vlan set to {}", this.vlan);
292
293 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700294 packetService.registerPacketListener(this);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700295 topology = topologyService.getTopology();
Ray Milkey269ffb92014-04-03 14:43:30 -0700296
297 //
298 // Event notification setup: channels and event handlers
299 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700300
301 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
302 arpReplyEventHandler,
303 Long.class,
304 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700305
TeruU3c049c42014-04-15 10:13:25 -0700306 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
307 arpCacheEventHandler,
308 String.class,
309 ArpCacheNotification.class);
310 arpCacheEventHandler.startUp();
311
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700312 Timer arpTimer = new Timer("arp-processing");
313 arpTimer.scheduleAtFixedRate(new TimerTask() {
314 @Override
315 public void run() {
316 doPeriodicArpProcessing();
317 }
TeruU3c049c42014-04-15 10:13:25 -0700318 }, 0, arpTimerPeriodConfig);
319
320 Timer arpCacheTimer = new Timer("arp-clearning");
321 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
322 @Override
323 public void run() {
324 doPeriodicArpCleaning();
325 }
326 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700327 }
328
329 /*
330 * Function that runs periodically to manage the asynchronous request mechanism.
331 * It basically cleans up old ARP requests if we don't get a response for them.
332 * The caller can designate that a request should be retried indefinitely, and
333 * this task will handle that as well.
334 */
335 private void doPeriodicArpProcessing() {
336 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
337 .<InetAddress, ArpRequest>create();
338
339 // Have to synchronize externally on the Multimap while using an
340 // iterator,
341 // even though it's a synchronizedMultimap
342 synchronized (arpRequests) {
343 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
344 .entries().iterator();
345
346 while (it.hasNext()) {
347 Map.Entry<InetAddress, ArpRequest> entry = it.next();
348 ArpRequest request = entry.getValue();
349 if (request.isExpired()) {
350 log.debug("Cleaning expired ARP request for {}", entry
351 .getKey().getHostAddress());
352
Ray Milkey269ffb92014-04-03 14:43:30 -0700353 // If the ARP request is expired and then delete the device
Ray Milkey269ffb92014-04-03 14:43:30 -0700354 HostArpRequester requester = (HostArpRequester) request.requester;
355 ARP req = requester.getArpRequest();
Jonathan Harte37e4e22014-05-13 19:12:02 -0700356 topology.acquireReadLock();
357 Device targetDev = topology.getDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
358 topology.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700359 if (targetDev != null) {
360 onosDeviceService.deleteOnosDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
361 if (log.isDebugEnabled()) {
362 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDev.getMacAddress());
363 }
364 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700365
366 it.remove();
367
368 if (request.shouldRetry()) {
369 retryList.put(entry.getKey(), request);
370 }
371 }
372 }
373 }
374
375 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
376 .asMap().entrySet()) {
377
378 InetAddress address = entry.getKey();
379
380 log.debug("Resending ARP request for {}", address.getHostAddress());
381
382 // Only ARP requests sent by the controller will have the retry flag
383 // set, so for now we can just send a new ARP request for that
384 // address.
385 sendArpRequestForAddress(address);
386
387 for (ArpRequest request : entry.getValue()) {
388 arpRequests.put(address, new ArpRequest(request));
389 }
390 }
391 }
392
393 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700394 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruU8b2d1672014-04-25 17:02:56 -0700395
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
Jonathan Harte37e4e22014-05-13 19:12:02 -0700456 topology.acquireReadLock();
457 Device targetDevice = topology.getDeviceByMac(
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700458 MACAddress.valueOf(arp.getTargetHardwareAddress()));
Jonathan Harte37e4e22014-05-13 19:12:02 -0700459 topology.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 Harte3702f22014-04-29 02:56:56 -0700468 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700469 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 Harte3702f22014-04-29 02:56:56 -0700493 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700494 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(
Jonathan Harte3702f22014-04-29 02:56:56 -0700513 eth, new SwitchPort(outSwitch, outPort));
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(
Jonathan Harte3702f22014-04-29 02:56:56 -0700578 eth, new SwitchPort(intf.getDpid(), intf.getPort()));
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 Harte3702f22014-04-29 02:56:56 -0700669 packetService.sendPacket(eth, new SwitchPort(dpid, port));
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() {
TeruU8b2d1672014-04-25 17:02:56 -0700703 return arpCache.getMappings();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700704 }
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 }
TeruU8b2d1672014-04-25 17:02:56 -0700740
741 public long getArpEntryTimeout() {
742 return arpCache.getArpEntryTimeout();
743 }
744
745 public long getArpCleaningTimerPeriod() {
746 return arpCleaningTimerPeriodConfig;
747 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700748}