blob: 503bf308ce1ef9254b41805d4c6e704d768abeb1 [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
16import net.floodlightcontroller.core.FloodlightContext;
17import net.floodlightcontroller.core.IFloodlightProviderService;
18import net.floodlightcontroller.core.IOFMessageListener;
19import net.floodlightcontroller.core.IOFSwitch;
Jonathan Harte93aed42013-12-05 18:39:50 -080020import net.floodlightcontroller.core.module.FloodlightModuleContext;
21import net.floodlightcontroller.core.module.IFloodlightModule;
22import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart5afde492013-10-01 12:30:53 +130023import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120024import net.floodlightcontroller.util.MACAddress;
Jonathan Hart8f6dc092014-04-18 15:56:43 -070025import net.onrc.onos.apps.sdnip.Interface;
Jonathan Hart6df90172014-04-03 10:13:11 -070026import net.onrc.onos.core.datagrid.IDatagridService;
27import net.onrc.onos.core.datagrid.IEventChannel;
28import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hart23701d12014-04-03 10:45:48 -070029import net.onrc.onos.core.devicemanager.IOnosDeviceService;
30import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070031import net.onrc.onos.core.main.config.IConfigInfoService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070032import net.onrc.onos.core.packet.ARP;
33import net.onrc.onos.core.packet.Ethernet;
34import net.onrc.onos.core.packet.IPv4;
Jonathan Hart313fdf02014-04-10 14:09:46 -070035import net.onrc.onos.core.packetservice.BroadcastPacketOutNotification;
36import net.onrc.onos.core.packetservice.SinglePacketOutNotification;
Jonathan Hart472062d2014-04-03 10:56:48 -070037import net.onrc.onos.core.topology.Device;
38import net.onrc.onos.core.topology.INetworkGraphService;
39import net.onrc.onos.core.topology.NetworkGraph;
40import net.onrc.onos.core.topology.Switch;
Jonathan Hart23701d12014-04-03 10:45:48 -070041import net.onrc.onos.core.util.Dpid;
Jonathan Hart23701d12014-04-03 10:45:48 -070042import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120043
44import org.openflow.protocol.OFMessage;
45import org.openflow.protocol.OFPacketIn;
46import org.openflow.protocol.OFPacketOut;
47import org.openflow.protocol.OFPort;
48import org.openflow.protocol.OFType;
49import org.openflow.protocol.action.OFAction;
50import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120051import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120052import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
Jonathan Hart4dfc3652013-08-02 20:22:36 +120055import com.google.common.collect.HashMultimap;
56import com.google.common.collect.Multimaps;
57import com.google.common.collect.SetMultimap;
58
Jonathan Hart18ad55c2013-11-11 22:49:55 -080059public class ProxyArpManager implements IProxyArpService, IOFMessageListener,
Ray Milkey269ffb92014-04-03 14:43:30 -070060 IFloodlightModule {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070061 private static final Logger log = LoggerFactory
62 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080063
TeruU3c049c42014-04-15 10:13:25 -070064 private static long arpTimerPeriodConfig = 100; // ms
65 private static int arpRequestTimeoutConfig = 2000; // ms
66 private long arpCleaningTimerPeriodConfig = 60 * 1000; // ms (1 min)
Jonathan Hartda4d0e12013-09-30 21:00:20 +130067
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070068 private IFloodlightProviderService floodlightProvider;
69 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070070 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
71 private IEventChannel<Long, BroadcastPacketOutNotification> broadcastPacketOutEventChannel;
72 private IEventChannel<Long, SinglePacketOutNotification> singlePacketOutEventChannel;
TeruU3c049c42014-04-15 10:13:25 -070073 private IEventChannel<String, ArpCacheNotification> arpCacheEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070074 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU7feef8a2014-04-03 00:15:49 -070075 private static final String BROADCAST_PACKET_OUT_CHANNEL_NAME = "onos.broadcast_packet_out";
76 private static final String SINGLE_PACKET_OUT_CHANNEL_NAME = "onos.single_packet_out";
TeruU3c049c42014-04-15 10:13:25 -070077 private static final String ARP_CACHE_CHANNEL_NAME = "onos.arp_cache";
78 private final ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
79 private final BroadcastPacketOutEventHandler broadcastPacketOutEventHandler = new BroadcastPacketOutEventHandler();
80 private final SinglePacketOutEventHandler singlePacketOutEventHandler = new SinglePacketOutEventHandler();
81 private final ArpCacheEventHandler arpCacheEventHandler = new ArpCacheEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070082
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070083 private IConfigInfoService configService;
84 private IRestApiService restApi;
85 private IFlowPusherService flowPusher;
Ray Milkey269ffb92014-04-03 14:43:30 -070086
87 private INetworkGraphService networkGraphService;
88 private NetworkGraph networkGraph;
89 private IOnosDeviceService onosDeviceService;
Jonathan Harte93aed42013-12-05 18:39:50 -080090
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070091 private short vlan;
92 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080093
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070094 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120095
TeruU3c049c42014-04-15 10:13:25 -070096 private ArpCache arpCache;
97
TeruU7feef8a2014-04-03 00:15:49 -070098 private class BroadcastPacketOutEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070099 IEventChannelListener<Long, BroadcastPacketOutNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700100
Ray Milkey269ffb92014-04-03 14:43:30 -0700101 @Override
102 public void entryAdded(BroadcastPacketOutNotification value) {
103 if (log.isTraceEnabled()) {
TeruU3c049c42014-04-15 10:13:25 -0700104 log.trace("entryAdded for BroadcastPacketOutEventHandler, ip{}, sw {}, port {}",
105 value.getTargetAddress(), value.getInSwitch(), value.getInPort());
Ray Milkey269ffb92014-04-03 14:43:30 -0700106 }
TeruU3c049c42014-04-15 10:13:25 -0700107 BroadcastPacketOutNotification notification = value;
Jonathan Hart313fdf02014-04-10 14:09:46 -0700108 broadcastArpRequestOutMyEdge(notification.getPacketData(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700109 notification.getInSwitch(),
110 notification.getInPort());
111
112 // set timestamp
TeruU3c049c42014-04-15 10:13:25 -0700113 //This 4 means ipv4 addr size. Need to change it in the future.
Ray Milkey269ffb92014-04-03 14:43:30 -0700114 ByteBuffer buffer = ByteBuffer.allocate(4);
115 buffer.putInt(notification.getTargetAddress());
116 InetAddress addr = null;
117 try {
118 addr = InetAddress.getByAddress(buffer.array());
119 } catch (UnknownHostException e) {
120 log.error("Exception:", e);
121 }
122
123 if (addr != null) {
124 for (ArpRequest request : arpRequests.get(addr)) {
125 request.setRequestTime();
126 }
127 }
128 }
129
130 @Override
131 public void entryUpdated(BroadcastPacketOutNotification value) {
TeruU3c049c42014-04-15 10:13:25 -0700132 log.debug("entryUpdated for BroadcastPacketOutEventHandler");
Ray Milkey269ffb92014-04-03 14:43:30 -0700133 entryAdded(value);
134 }
135
136 @Override
137 public void entryRemoved(BroadcastPacketOutNotification value) {
TeruU3c049c42014-04-15 10:13:25 -0700138 //Not implemented. BroadcastPacketOutNotification is used only for remote messaging.
Ray Milkey269ffb92014-04-03 14:43:30 -0700139 }
TeruU7feef8a2014-04-03 00:15:49 -0700140 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700141
TeruU7feef8a2014-04-03 00:15:49 -0700142 private class SinglePacketOutEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -0700143 IEventChannelListener<Long, SinglePacketOutNotification> {
144 @Override
145 public void entryAdded(SinglePacketOutNotification packetOutNotification) {
TeruU3c049c42014-04-15 10:13:25 -0700146 log.debug("entryAdded for SinglePacketOutEventHandler");
Ray Milkey269ffb92014-04-03 14:43:30 -0700147 SinglePacketOutNotification notification =
TeruU3c049c42014-04-15 10:13:25 -0700148 packetOutNotification;
Jonathan Hart313fdf02014-04-10 14:09:46 -0700149 sendArpRequestOutPort(notification.getPacketData(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700150 notification.getOutSwitch(),
151 notification.getOutPort());
152
153 // set timestamp
TeruU3c049c42014-04-15 10:13:25 -0700154 //This 4 means ipv4 addr size. Need to change it in the future.
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 ByteBuffer buffer = ByteBuffer.allocate(4);
156 buffer.putInt(notification.getTargetAddress());
157 InetAddress addr = null;
158 try {
159 addr = InetAddress.getByAddress(buffer.array());
160 } catch (UnknownHostException e) {
161 log.error("Exception:", e);
162 }
163
164 if (addr != null) {
165 for (ArpRequest request : arpRequests.get(addr)) {
166 request.setRequestTime();
167 }
168 }
169 }
170
171 @Override
172 public void entryUpdated(SinglePacketOutNotification packetOutNotification) {
TeruU3c049c42014-04-15 10:13:25 -0700173 log.debug("entryUpdated for SinglePacketOutEventHandler");
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 entryAdded(packetOutNotification);
175 }
176
177 @Override
178 public void entryRemoved(SinglePacketOutNotification packetOutNotification) {
TeruU3c049c42014-04-15 10:13:25 -0700179 //Not implemented. SinglePacketOutNotification is used only for remote messaging.
Ray Milkey269ffb92014-04-03 14:43:30 -0700180 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700181 }
182
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700183 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -0700184 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700185
Ray Milkey269ffb92014-04-03 14:43:30 -0700186 @Override
187 public void entryAdded(ArpReplyNotification arpReply) {
188 log.debug("Received ARP reply notification for ip {}, mac {}",
189 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
TeruU3c049c42014-04-15 10:13:25 -0700190 //This 4 means ipv4 addr size. Need to change it in the future.
Ray Milkey269ffb92014-04-03 14:43:30 -0700191 ByteBuffer buffer = ByteBuffer.allocate(4);
192 buffer.putInt(arpReply.getTargetAddress());
193 InetAddress addr = null;
194 try {
195 addr = InetAddress.getByAddress(buffer.array());
196 } catch (UnknownHostException e) {
197 log.error("Exception:", e);
198 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700199
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 if (addr != null) {
201 sendArpReplyToWaitingRequesters(addr,
202 arpReply.getTargetMacAddress());
203 }
204 }
205
206 @Override
207 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700208 entryAdded(arpReply);
209 }
210
211 @Override
212 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -0700213 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
214 }
215 }
216
217 private class ArpCacheEventHandler implements
218 IEventChannelListener<String, ArpCacheNotification> {
219
220 /**
221 * Startup processing.
222 */
223 private void startUp() {
224 //
225 // TODO: Read all state from the database:
226 // For now, as a shortcut we read it from the datagrid
227 //
228 Collection<ArpCacheNotification> arpCacheEvents =
229 arpCacheEventChannel.getAllEntries();
230
231 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
232 entryAdded(arpCacheEvent);
233 }
234 }
235
236 @Override
237 public void entryAdded(ArpCacheNotification value) {
238
239 try {
240 log.debug("Received entryAdded for ARP cache notification for ip {}, mac {}",
241 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
242 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
243 } catch (UnknownHostException e) {
244 log.error("Exception : ", e);
245 }
246 }
247
248 @Override
249 public void entryRemoved(ArpCacheNotification value) {
250 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
251 value.getTargetAddress(), value.getTargetMacAddress());
252 try {
253 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
254 } catch (UnknownHostException e) {
255 log.error("Exception : ", e);
256 }
257 }
258
259 @Override
260 public void entryUpdated(ArpCacheNotification value) {
261 try {
262 log.debug("Received entryUpdated for ARP cache notification for ip {}, mac {}",
263 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
264 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
265 } catch (UnknownHostException e) {
266 log.error("Exception : ", e);
267 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700269 }
270
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700271 private static class ArpRequest {
272 private final IArpRequester requester;
273 private final boolean retry;
274 private boolean sent = false;
275 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200276
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700277 public ArpRequest(IArpRequester requester, boolean retry) {
278 this.requester = requester;
279 this.retry = retry;
280 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200281
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700282 public ArpRequest(ArpRequest old) {
283 this.requester = old.requester;
284 this.retry = old.retry;
285 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200286
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700287 public boolean isExpired() {
288 return sent
TeruU3c049c42014-04-15 10:13:25 -0700289 && ((System.currentTimeMillis() - requestTime) > arpRequestTimeoutConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700290 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200291
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700292 public boolean shouldRetry() {
293 return retry;
294 }
295
296 public void dispatchReply(InetAddress ipAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700297 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700298 requester.arpResponse(ipAddress, replyMacAddress);
299 }
300
301 public void setRequestTime() {
302 this.requestTime = System.currentTimeMillis();
303 this.sent = true;
304 }
305 }
306
307 private class HostArpRequester implements IArpRequester {
308 private final ARP arpRequest;
309 private final long dpid;
310 private final short port;
311
312 public HostArpRequester(ARP arpRequest, long dpid, short port) {
313 this.arpRequest = arpRequest;
314 this.dpid = dpid;
315 this.port = port;
316 }
317
318 @Override
319 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
320 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
321 macAddress);
322 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700323
324 public ARP getArpRequest() {
325 return arpRequest;
326 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700327 }
328
329 @Override
330 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
331 Collection<Class<? extends IFloodlightService>> l =
332 new ArrayList<Class<? extends IFloodlightService>>();
333 l.add(IProxyArpService.class);
334 return l;
335 }
336
337 @Override
338 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
339 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
340 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
341 m.put(IProxyArpService.class, this);
342 return m;
343 }
344
345 @Override
346 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
347 Collection<Class<? extends IFloodlightService>> dependencies =
348 new ArrayList<Class<? extends IFloodlightService>>();
349 dependencies.add(IFloodlightProviderService.class);
350 dependencies.add(IRestApiService.class);
351 dependencies.add(IDatagridService.class);
352 dependencies.add(IConfigInfoService.class);
353 dependencies.add(IFlowPusherService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700354 dependencies.add(INetworkGraphService.class);
355 dependencies.add(IOnosDeviceService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700356 return dependencies;
357 }
358
359 @Override
360 public void init(FloodlightModuleContext context) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700361 this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700362 this.configService = context.getServiceImpl(IConfigInfoService.class);
363 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700364 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700365 this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700366 this.networkGraphService = context.getServiceImpl(INetworkGraphService.class);
367 this.onosDeviceService = context.getServiceImpl(IOnosDeviceService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700368
TeruU3c049c42014-04-15 10:13:25 -0700369 Map<String, String> configOptions = context.getConfigParams(this);
370 Long agingmsec = null;
371 try {
372 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
373 } catch (NumberFormatException e) {
374 log.debug("ArpEntryTimeout related config options were not set. Use default.");
375 }
376
377 arpCache = new ArpCache();
378 if (agingmsec != null) {
379 arpCache.setArpEntryTimeoutConfig(agingmsec);
380 }
381
382 try {
383 arpCleaningTimerPeriodConfig = Long.parseLong(configOptions.get("cleanupmsec"));
384 } catch (NumberFormatException e) {
385 log.debug("ArpCleaningTimerPeriod related config options were not set. Use default.");
386 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700387
388 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
389 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700390 }
391
392 @Override
393 public void startUp(FloodlightModuleContext context) {
394 this.vlan = configService.getVlan();
395 log.info("vlan set to {}", this.vlan);
396
397 restApi.addRestletRoutable(new ArpWebRoutable());
398 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
Ray Milkey269ffb92014-04-03 14:43:30 -0700399 networkGraph = networkGraphService.getNetworkGraph();
400
401 //
402 // Event notification setup: channels and event handlers
403 //
404 broadcastPacketOutEventChannel = datagrid.addListener(BROADCAST_PACKET_OUT_CHANNEL_NAME,
405 broadcastPacketOutEventHandler,
406 Long.class,
407 BroadcastPacketOutNotification.class);
408
409 singlePacketOutEventChannel = datagrid.addListener(SINGLE_PACKET_OUT_CHANNEL_NAME,
410 singlePacketOutEventHandler,
411 Long.class,
412 SinglePacketOutNotification.class);
413
414 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
415 arpReplyEventHandler,
416 Long.class,
417 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700418
TeruU3c049c42014-04-15 10:13:25 -0700419 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
420 arpCacheEventHandler,
421 String.class,
422 ArpCacheNotification.class);
423 arpCacheEventHandler.startUp();
424
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700425 Timer arpTimer = new Timer("arp-processing");
426 arpTimer.scheduleAtFixedRate(new TimerTask() {
427 @Override
428 public void run() {
429 doPeriodicArpProcessing();
430 }
TeruU3c049c42014-04-15 10:13:25 -0700431 }, 0, arpTimerPeriodConfig);
432
433 Timer arpCacheTimer = new Timer("arp-clearning");
434 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
435 @Override
436 public void run() {
437 doPeriodicArpCleaning();
438 }
439 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700440 }
441
442 /*
443 * Function that runs periodically to manage the asynchronous request mechanism.
444 * It basically cleans up old ARP requests if we don't get a response for them.
445 * The caller can designate that a request should be retried indefinitely, and
446 * this task will handle that as well.
447 */
448 private void doPeriodicArpProcessing() {
449 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
450 .<InetAddress, ArpRequest>create();
451
452 // Have to synchronize externally on the Multimap while using an
453 // iterator,
454 // even though it's a synchronizedMultimap
455 synchronized (arpRequests) {
456 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
457 .entries().iterator();
458
459 while (it.hasNext()) {
460 Map.Entry<InetAddress, ArpRequest> entry = it.next();
461 ArpRequest request = entry.getValue();
462 if (request.isExpired()) {
463 log.debug("Cleaning expired ARP request for {}", entry
464 .getKey().getHostAddress());
465
Ray Milkey269ffb92014-04-03 14:43:30 -0700466 // If the ARP request is expired and then delete the device
Ray Milkey269ffb92014-04-03 14:43:30 -0700467 HostArpRequester requester = (HostArpRequester) request.requester;
468 ARP req = requester.getArpRequest();
TeruU3c049c42014-04-15 10:13:25 -0700469 networkGraph.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700470 Device targetDev = networkGraph.getDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700471 networkGraph.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700472 if (targetDev != null) {
473 onosDeviceService.deleteOnosDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
474 if (log.isDebugEnabled()) {
475 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDev.getMacAddress());
476 }
477 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700478
479 it.remove();
480
481 if (request.shouldRetry()) {
482 retryList.put(entry.getKey(), request);
483 }
484 }
485 }
486 }
487
488 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
489 .asMap().entrySet()) {
490
491 InetAddress address = entry.getKey();
492
493 log.debug("Resending ARP request for {}", address.getHostAddress());
494
495 // Only ARP requests sent by the controller will have the retry flag
496 // set, so for now we can just send a new ARP request for that
497 // address.
498 sendArpRequestForAddress(address);
499
500 for (ArpRequest request : entry.getValue()) {
501 arpRequests.put(address, new ArpRequest(request));
502 }
503 }
504 }
505
506 @Override
507 public String getName() {
508 return "proxyarpmanager";
509 }
510
511 @Override
512 public boolean isCallbackOrderingPrereq(OFType type, String name) {
513 if (type == OFType.PACKET_IN) {
514 return "devicemanager".equals(name)
515 || "onosdevicemanager".equals(name);
516 } else {
517 return false;
518 }
519 }
520
521 @Override
522 public boolean isCallbackOrderingPostreq(OFType type, String name) {
523 return type == OFType.PACKET_IN && "onosforwarding".equals(name);
524 }
525
526 @Override
527 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700528 if (!(msg instanceof OFPacketIn)) {
529 return Command.CONTINUE;
530 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700531
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700532 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200533 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200534
TeruU3c049c42014-04-15 10:13:25 -0700535 return classifyPacket(sw, msg, eth);
536 }
537
538 protected Command classifyPacket(IOFSwitch sw, OFMessage msg, Ethernet eth) {
539 OFPacketIn pi = (OFPacketIn) msg;
540
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700541 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
542 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700543 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700544 if (arp.getOpCode() == ARP.OP_REQUEST) {
545 handleArpRequest(sw, pi, arp, eth);
546 } else if (arp.getOpCode() == ARP.OP_REPLY) {
547 // For replies we simply send a notification via Hazelcast
548 sendArpReplyNotification(eth, pi);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700549 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700550 // Stop ARP packets here
551 return Command.STOP;
552 }
pingping-linb8757bf2013-12-13 01:48:58 +0800553
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700554 // Propagate everything else
555 return Command.CONTINUE;
556 }
pingping-linb8757bf2013-12-13 01:48:58 +0800557
TeruU3c049c42014-04-15 10:13:25 -0700558 private void learnArp(ARP arp) {
559 ArpCacheNotification arpCacheNotification = null;
560
561 arpCacheNotification = new ArpCacheNotification(arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
562
563 try {
564 arpCacheEventChannel.addEntry(InetAddress.getByAddress(arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
565 } catch (UnknownHostException e) {
566 log.error("Exception : ", e);
567 }
568 }
569
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700570 private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
Ray Milkey269ffb92014-04-03 14:43:30 -0700571 Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700572 if (log.isTraceEnabled()) {
573 log.trace("ARP request received for {}",
574 inetAddressToString(arp.getTargetProtocolAddress()));
575 }
pingping-linb8757bf2013-12-13 01:48:58 +0800576
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700577 InetAddress target;
578 try {
579 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
580 } catch (UnknownHostException e) {
581 log.debug("Invalid address in ARP request", e);
582 return;
583 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700584
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700585 if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
586 // If the request came from outside our network, we only care if
587 // it was a request for one of our interfaces.
588 if (configService.isInterfaceAddress(target)) {
589 log.trace(
590 "ARP request for our interface. Sending reply {} => {}",
591 target.getHostAddress(),
592 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800593
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700594 sendArpReply(arp, sw.getId(), pi.getInPort(),
595 configService.getRouterMacAddress());
596 }
pingping-linb8757bf2013-12-13 01:48:58 +0800597
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700598 return;
599 }
pingping-linb8757bf2013-12-13 01:48:58 +0800600
TeruU3c049c42014-04-15 10:13:25 -0700601 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800602
TeruU3c049c42014-04-15 10:13:25 -0700603 arpRequests.put(target, new ArpRequest(new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800604
TeruU3c049c42014-04-15 10:13:25 -0700605 networkGraph.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700606 Device targetDevice = networkGraph.getDeviceByMac(MACAddress.valueOf(arp.getTargetHardwareAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700607 networkGraph.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800608
Ray Milkey269ffb92014-04-03 14:43:30 -0700609 if (targetDevice == null) {
610 if (log.isTraceEnabled()) {
611 log.trace("No device info found for {} - broadcasting",
612 target.getHostAddress());
613 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800614
Ray Milkey269ffb92014-04-03 14:43:30 -0700615 // We don't know the device so broadcast the request out
TeruU3c049c42014-04-15 10:13:25 -0700616 BroadcastPacketOutNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700617 new BroadcastPacketOutNotification(eth.serialize(),
618 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort());
619 log.debug("broadcastPacketOutEventChannel mac {}, ip {}, dpid {}, port {}, paket {}", eth.getSourceMAC().toLong(),
620 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort(), eth.serialize().length);
TeruU3c049c42014-04-15 10:13:25 -0700621 broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700622 } else {
623 // Even if the device exists in our database, we do not reply to
624 // the request directly, but check whether the device is still valid
625 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200626
Ray Milkey269ffb92014-04-03 14:43:30 -0700627 if (log.isTraceEnabled()) {
628 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
629 new Object[]{
630 inetAddressToString(arp.getTargetProtocolAddress()),
631 macAddress,
632 HexString.toHexString(sw.getId()), pi.getInPort()});
633 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200634
Ray Milkey269ffb92014-04-03 14:43:30 -0700635 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800636
Ray Milkey269ffb92014-04-03 14:43:30 -0700637 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
638
639 if (!outPorts.iterator().hasNext()) {
640 if (log.isTraceEnabled()) {
641 log.trace("Device {} exists but is not connected to any ports" +
642 " - broadcasting", macAddress);
643 }
644
TeruU3c049c42014-04-15 10:13:25 -0700645 BroadcastPacketOutNotification value =
646 new BroadcastPacketOutNotification(eth.serialize(),
647 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort());
648 broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700649 } else {
650 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700651
652 if (portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
653 continue;
654 }
655
656 short outPort = portObject.getNumber().shortValue();
657 Switch outSwitchObject = portObject.getSwitch();
658 long outSwitch = outSwitchObject.getDpid();
659
660 if (log.isTraceEnabled()) {
661 log.trace("Probing device {} on port {}/{}",
662 new Object[]{macAddress,
663 HexString.toHexString(outSwitch), outPort});
664 }
665
TeruU3c049c42014-04-15 10:13:25 -0700666 SinglePacketOutNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700667 new SinglePacketOutNotification(eth.serialize(),
668 ByteBuffer.wrap(target.getAddress()).getInt(), outSwitch, outPort);
TeruU3c049c42014-04-15 10:13:25 -0700669 singlePacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700670 }
671 }
672 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700673 }
pingping-linb8757bf2013-12-13 01:48:58 +0800674
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700675 private void sendArpRequestForAddress(InetAddress ipAddress) {
676 // TODO what should the sender IP address and MAC address be if no
677 // IP addresses are configured? Will there ever be a need to send
678 // ARP requests from the controller in that case?
679 // All-zero MAC address doesn't seem to work - hosts don't respond to it
680
681 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
682 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
683 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
684 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
685 (byte) 0xff, (byte) 0xff, (byte) 0xff};
686
687 ARP arpRequest = new ARP();
688
689 arpRequest
690 .setHardwareType(ARP.HW_TYPE_ETHERNET)
691 .setProtocolType(ARP.PROTO_TYPE_IP)
692 .setHardwareAddressLength(
693 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
694 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
695 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
696 .setTargetProtocolAddress(ipAddress.getAddress());
697
698 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700699 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700700 byte[] senderMacAddress = genericNonZeroMac;
701 if (routerMacAddress != null) {
702 senderMacAddress = routerMacAddress.toBytes();
703 }
704 arpRequest.setSenderHardwareAddress(senderMacAddress);
705
706 byte[] senderIPAddress = zeroIpv4;
707 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700708 if (intf == null) {
709 // TODO handle the case where the controller needs to send an ARP
710 // request but there's not IP configuration. In this case the
711 // request should be broadcast out all edge ports in the network.
712 log.warn("Sending ARP requests with default configuration "
713 + "not supported");
714 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700715 }
716
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700717 senderIPAddress = intf.getIpAddress().getAddress();
718
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700719 arpRequest.setSenderProtocolAddress(senderIPAddress);
720
721 Ethernet eth = new Ethernet();
722 eth.setSourceMACAddress(senderMacAddress)
723 .setDestinationMACAddress(broadcastMac)
724 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
725
726 if (vlan != NO_VLAN) {
727 eth.setVlanID(vlan).setPriorityCode((byte) 0);
728 }
729
730 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700731 SinglePacketOutNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700732 new SinglePacketOutNotification(eth.serialize(), ByteBuffer.wrap(ipAddress.getAddress()).getInt(),
733 intf.getDpid(), intf.getPort());
TeruU3c049c42014-04-15 10:13:25 -0700734
735 singlePacketOutEventChannel.addTransientEntry(MACAddress.valueOf(senderMacAddress).toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700736 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700737
TeruU3c049c42014-04-15 10:13:25 -0700738 //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 -0700739 /*
TeruU7feef8a2014-04-03 00:15:49 -0700740 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700741 sendArpRequestToSwitches(dstAddress, arpRequest,
742 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700743 }
744
TeruU3c049c42014-04-15 10:13:25 -0700745 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
746 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700747
748 if (configService.hasLayer3Configuration()) {
749 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700750 if (intf != null) {
751 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
752 }
753 else {
754 //TODO here it should be broadcast out all non-interface edge ports.
755 //I think we can assume that if it's not a request for an external
756 //network, it's an ARP for a host in our own network. So we want to
757 //send it out all edge ports that don't have an interface configured
758 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700759 log.debug("No interface found to send ARP request for {}",
760 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700761 }
TeruU3c049c42014-04-15 10:13:25 -0700762 }
763 else {
764 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700765 }
766 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700767 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700768
769 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
770 ARP arp = (ARP) eth.getPayload();
771
772 if (log.isTraceEnabled()) {
773 log.trace("Sending ARP reply for {} to other ONOS instances",
774 inetAddressToString(arp.getSenderProtocolAddress()));
775 }
776
777 InetAddress targetAddress;
778
779 try {
780 targetAddress = InetAddress.getByAddress(arp
781 .getSenderProtocolAddress());
782 } catch (UnknownHostException e) {
783 log.error("Unknown host", e);
784 return;
785 }
786
787 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
788
TeruU3c049c42014-04-15 10:13:25 -0700789 ArpReplyNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700790 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
791 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
TeruU3c049c42014-04-15 10:13:25 -0700792 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700793 }
794
795 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
Ray Milkey269ffb92014-04-03 14:43:30 -0700796 short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700797 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
798
799 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
800
801 OFPacketOut po = new OFPacketOut();
802 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
803 .setPacketData(arpRequest);
804
805 List<OFAction> actions = new ArrayList<OFAction>();
806
TeruU3c049c42014-04-15 10:13:25 -0700807 networkGraph.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700808 Switch graphSw = networkGraph.getSwitch(sw.getId());
TeruU3c049c42014-04-15 10:13:25 -0700809 networkGraph.releaseReadLock();
810
Ray Milkey269ffb92014-04-03 14:43:30 -0700811 Collection<net.onrc.onos.core.topology.Port> ports = graphSw.getPorts();
812
813 if (ports == null) {
814 continue;
815 }
816
817 for (net.onrc.onos.core.topology.Port portObject : ports) {
818 if (portObject.getOutgoingLink() == null && portObject.getNumber() > 0) {
819 Long portNumber = portObject.getNumber();
820
821 if (sw.getId() == inSwitch && portNumber.shortValue() == inPort) {
822 // This is the port that the ARP message came in,
823 // so don't broadcast out this port
824 continue;
825 }
826 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
827 new net.onrc.onos.core.util.Port(portNumber.shortValue())));
828 actions.add(new OFActionOutput(portNumber.shortValue()));
829 }
830 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700831
832 po.setActions(actions);
833 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
834 po.setActionsLength(actionsLength);
835 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
836 + arpRequest.length);
837
838 flowPusher.add(sw, po);
839 }
840
841 if (log.isTraceEnabled()) {
842 log.trace("Broadcast ARP request to: {}", switchPorts);
843 }
844 }
845
846 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
847 if (log.isTraceEnabled()) {
848 log.trace("Sending ARP request out {}/{}",
849 HexString.toHexString(dpid), port);
850 }
851
852 OFPacketOut po = new OFPacketOut();
853 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
854 .setPacketData(arpRequest);
855
856 List<OFAction> actions = new ArrayList<OFAction>();
857 actions.add(new OFActionOutput(port));
858 po.setActions(actions);
859 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
860 po.setActionsLength(actionsLength);
861 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
862 + arpRequest.length);
863
864 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
865
866 if (sw == null) {
867 log.warn("Switch not found when sending ARP request");
868 return;
869 }
870
871 flowPusher.add(sw, po);
872 }
873
874 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700875 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700876 if (log.isTraceEnabled()) {
877 log.trace(
878 "Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700879 new Object[]{
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700880 inetAddressToString(arpRequest
881 .getTargetProtocolAddress()),
882 targetMac,
883 inetAddressToString(arpRequest
884 .getSenderProtocolAddress())});
885 }
886
887 ARP arpReply = new ARP();
888 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
889 .setProtocolType(ARP.PROTO_TYPE_IP)
890 .setHardwareAddressLength(
891 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
892 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
893 .setOpCode(ARP.OP_REPLY)
894 .setSenderHardwareAddress(targetMac.toBytes())
895 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
896 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
897 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
898
899 Ethernet eth = new Ethernet();
900 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
901 .setSourceMACAddress(targetMac.toBytes())
902 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
903
904 if (vlan != NO_VLAN) {
905 eth.setVlanID(vlan).setPriorityCode((byte) 0);
906 }
907
908 List<OFAction> actions = new ArrayList<OFAction>();
909 actions.add(new OFActionOutput(port));
910
911 OFPacketOut po = new OFPacketOut();
912 po.setInPort(OFPort.OFPP_NONE)
913 .setBufferId(-1)
914 .setPacketData(eth.serialize())
915 .setActions(actions)
916 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
917 .setLengthU(
918 OFPacketOut.MINIMUM_LENGTH
919 + OFActionOutput.MINIMUM_LENGTH
920 + po.getPacketData().length);
921
922 List<OFMessage> msgList = new ArrayList<OFMessage>();
923 msgList.add(po);
924
925 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
926
927 if (sw == null) {
928 log.warn("Switch {} not found when sending ARP reply",
929 HexString.toHexString(dpid));
930 return;
931 }
932
933 flowPusher.add(sw, po);
934 }
935
936 private String inetAddressToString(byte[] bytes) {
937 try {
938 return InetAddress.getByAddress(bytes).getHostAddress();
939 } catch (UnknownHostException e) {
940 log.debug("Invalid IP address", e);
941 return "";
942 }
943 }
944
945 /*
946 * IProxyArpService methods
947 */
948
949 @Override
950 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700951 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700952 }
953
954 @Override
955 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700956 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700957 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
958
959 // Sanity check to make sure we don't send a request for our own address
960 if (!configService.isInterfaceAddress(ipAddress)) {
961 sendArpRequestForAddress(ipAddress);
962 }
963 }
964
965 @Override
966 public List<String> getMappings() {
967 return new ArrayList<String>();
968 }
969
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700970 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700971 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700972 log.debug("Sending ARP reply for {} to requesters",
973 address.getHostAddress());
974
975 // See if anyone's waiting for this ARP reply
976 Set<ArpRequest> requests = arpRequests.get(address);
977
978 // Synchronize on the Multimap while using an iterator for one of the
979 // sets
980 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
981 requests.size());
982 synchronized (arpRequests) {
983 Iterator<ArpRequest> it = requests.iterator();
984 while (it.hasNext()) {
985 ArpRequest request = it.next();
986 it.remove();
987 requestsToSend.add(request);
988 }
989 }
990
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700991 // Don't hold an ARP lock while dispatching requests
992 for (ArpRequest request : requestsToSend) {
993 request.dispatchReply(address, mac);
994 }
995 }
TeruU3c049c42014-04-15 10:13:25 -0700996
997 private void doPeriodicArpCleaning() {
998 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
999 for (InetAddress expireIp : expiredipslist) {
1000 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
1001 arpCacheEventChannel.removeEntry(expireIp.toString());
1002 }
1003 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001004}