blob: 2a897a515ed689cefafb440066bf86603c478c57 [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 Hart382623d2014-04-03 09:48:11 -070025import net.onrc.onos.apps.bgproute.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 Hartebba1e12013-10-29 11:37:02 -070029import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
TeruU7feef8a2014-04-03 00:15:49 -070030import net.onrc.onos.ofcontroller.devicemanager.IOnosDeviceService;
Jonathan Hart7804bea2014-01-07 10:50:52 -080031import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
TeruU7feef8a2014-04-03 00:15:49 -070032import net.onrc.onos.ofcontroller.networkgraph.Device;
33import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
34import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
35import net.onrc.onos.ofcontroller.networkgraph.Switch;
36import net.onrc.onos.ofcontroller.util.Dpid;
37import net.onrc.onos.ofcontroller.util.Port;
Jonathan Hartba9ced92013-11-24 16:52:13 -080038import net.onrc.onos.ofcontroller.util.SwitchPort;
Jonathan Hart96892d12014-03-26 20:21:29 -070039import net.onrc.onos.packet.ARP;
40import net.onrc.onos.packet.Ethernet;
41import net.onrc.onos.packet.IPv4;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120042
43import org.openflow.protocol.OFMessage;
44import org.openflow.protocol.OFPacketIn;
45import org.openflow.protocol.OFPacketOut;
46import org.openflow.protocol.OFPort;
47import org.openflow.protocol.OFType;
48import org.openflow.protocol.action.OFAction;
49import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120050import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Jonathan Hart4dfc3652013-08-02 20:22:36 +120054import com.google.common.collect.HashMultimap;
55import com.google.common.collect.Multimaps;
56import com.google.common.collect.SetMultimap;
57
Jonathan Hart18ad55c2013-11-11 22:49:55 -080058public class ProxyArpManager implements IProxyArpService, IOFMessageListener,
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070059 IFloodlightModule {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070060 private static final Logger log = LoggerFactory
61 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080062
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070063 private static final long ARP_TIMER_PERIOD = 100; // ms
Jonathan Hartdf6ec332013-08-04 01:37:14 +120064
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070065 private static final int ARP_REQUEST_TIMEOUT = 2000; // ms
Jonathan Hartda4d0e12013-09-30 21:00:20 +130066
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070067 private IFloodlightProviderService floodlightProvider;
68 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070069 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
70 private IEventChannel<Long, BroadcastPacketOutNotification> broadcastPacketOutEventChannel;
71 private IEventChannel<Long, SinglePacketOutNotification> singlePacketOutEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070072 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU7feef8a2014-04-03 00:15:49 -070073 private static final String BROADCAST_PACKET_OUT_CHANNEL_NAME = "onos.broadcast_packet_out";
74 private static final String SINGLE_PACKET_OUT_CHANNEL_NAME = "onos.single_packet_out";
75 private ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
76 private BroadcastPacketOutEventHandler broadcastPacketOutEventHandler = new BroadcastPacketOutEventHandler();
77 private SinglePacketOutEventHandler singlePacketOutEventHandler = new SinglePacketOutEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070078
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070079 private IConfigInfoService configService;
80 private IRestApiService restApi;
81 private IFlowPusherService flowPusher;
TeruU7feef8a2014-04-03 00:15:49 -070082
83 private INetworkGraphService networkGraphService;
84 private NetworkGraph networkGraph;
85 private IOnosDeviceService onosDeviceService;
Jonathan Harte93aed42013-12-05 18:39:50 -080086
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070087 private short vlan;
88 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080089
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070090 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120091
TeruU7feef8a2014-04-03 00:15:49 -070092 private class BroadcastPacketOutEventHandler implements
93 IEventChannelListener<Long, BroadcastPacketOutNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070094
TeruU7feef8a2014-04-03 00:15:49 -070095 @Override
96 public void entryAdded(BroadcastPacketOutNotification value) {
97 if(log.isTraceEnabled()) {
98 log.trace("entryAdded ip{}, sw {}, port {}, packet {}", value.getTargetAddress(), value.getInSwitch(), value.getInPort(), value.packet.length);
99 }
100 BroadcastPacketOutNotification notification = (BroadcastPacketOutNotification) value;
101 broadcastArpRequestOutMyEdge(notification.packet,
102 notification.getInSwitch(),
103 notification.getInPort());
104
105 // set timestamp
106 ByteBuffer buffer = ByteBuffer.allocate(4);
107 buffer.putInt(notification.getTargetAddress());
108 InetAddress addr = null;
109 try {
110 addr = InetAddress.getByAddress(buffer.array());
111 } catch (UnknownHostException e) {
112 log.error("Exception:", e);
113 }
114
115 if (addr != null) {
116 for (ArpRequest request : arpRequests.get(addr)) {
117 request.setRequestTime();
118 }
119 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700120 }
TeruU7feef8a2014-04-03 00:15:49 -0700121
122 @Override
123 public void entryUpdated(BroadcastPacketOutNotification value) {
124 log.debug("entryUpdated");
125 // TODO: For now, entryUpdated() is processed as entryAdded()
126 entryAdded(value);
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700127 }
TeruU7feef8a2014-04-03 00:15:49 -0700128
129 @Override
130 public void entryRemoved(BroadcastPacketOutNotification value) {
131 log.debug("entryRemoved");
132 // TODO: Not implemented. Revisit when this module is refactored
133 }
134 }
135
136 private class SinglePacketOutEventHandler implements
137 IEventChannelListener<Long, SinglePacketOutNotification> {
138 @Override
139 public void entryAdded(SinglePacketOutNotification packetOutNotification) {
140 log.debug("entryAdded");
141 SinglePacketOutNotification notification =
142 (SinglePacketOutNotification) packetOutNotification;
143 sendArpRequestOutPort(notification.packet,
144 notification.getOutSwitch(),
145 notification.getOutPort());
146
147 // set timestamp
148 ByteBuffer buffer = ByteBuffer.allocate(4);
149 buffer.putInt(notification.getTargetAddress());
150 InetAddress addr = null;
151 try {
152 addr = InetAddress.getByAddress(buffer.array());
153 } catch (UnknownHostException e) {
154 log.error("Exception:", e);
155 }
156
157 if (addr != null) {
158 for (ArpRequest request : arpRequests.get(addr)) {
159 request.setRequestTime();
160 }
161 }
162 }
163
164 @Override
165 public void entryUpdated(SinglePacketOutNotification packetOutNotification) {
166 log.debug("entryUpdated");
167 // TODO: For now, entryUpdated() is processed as entryAdded()
168 entryAdded(packetOutNotification);
169 }
170
171 @Override
172 public void entryRemoved(SinglePacketOutNotification packetOutNotification) {
173 log.debug("entryRemoved");
174 // TODO: Not implemented. Revisit when this module is refactored
175 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700176 }
177
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700178 private class ArpReplyEventHandler implements
TeruU7feef8a2014-04-03 00:15:49 -0700179 IEventChannelListener<Long, ArpReplyNotification> {
180
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700181 @Override
182 public void entryAdded(ArpReplyNotification arpReply) {
TeruU7feef8a2014-04-03 00:15:49 -0700183 log.debug("Received ARP reply notification for ip {}, mac {}",
184 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
185 ByteBuffer buffer = ByteBuffer.allocate(4);
186 buffer.putInt(arpReply.getTargetAddress());
187 InetAddress addr = null;
188 try {
189 addr = InetAddress.getByAddress(buffer.array());
190 } catch (UnknownHostException e) {
191 log.error("Exception:", e);
192 }
193
194 if(addr != null) {
195 sendArpReplyToWaitingRequesters(addr,
196 arpReply.getTargetMacAddress());
197 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700198 }
199
200 @Override
201 public void entryUpdated(ArpReplyNotification arpReply) {
202 // TODO: For now, entryUpdated() is processed as entryAdded()
203 entryAdded(arpReply);
204 }
205
206 @Override
207 public void entryRemoved(ArpReplyNotification arpReply) {
208 // TODO: Not implemented. Revisit when this module is refactored
209 }
210 }
211
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700212 private static class ArpRequest {
213 private final IArpRequester requester;
214 private final boolean retry;
215 private boolean sent = false;
216 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200217
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700218 public ArpRequest(IArpRequester requester, boolean retry) {
219 this.requester = requester;
220 this.retry = retry;
221 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200222
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700223 public ArpRequest(ArpRequest old) {
224 this.requester = old.requester;
225 this.retry = old.retry;
226 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200227
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700228 public boolean isExpired() {
229 return sent
230 && ((System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT);
231 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200232
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700233 public boolean shouldRetry() {
234 return retry;
235 }
236
237 public void dispatchReply(InetAddress ipAddress,
238 MACAddress replyMacAddress) {
239 requester.arpResponse(ipAddress, replyMacAddress);
240 }
241
242 public void setRequestTime() {
243 this.requestTime = System.currentTimeMillis();
244 this.sent = true;
245 }
246 }
247
248 private class HostArpRequester implements IArpRequester {
249 private final ARP arpRequest;
250 private final long dpid;
251 private final short port;
252
253 public HostArpRequester(ARP arpRequest, long dpid, short port) {
254 this.arpRequest = arpRequest;
255 this.dpid = dpid;
256 this.port = port;
257 }
258
259 @Override
260 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
261 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
262 macAddress);
263 }
TeruU7feef8a2014-04-03 00:15:49 -0700264
265 public ARP getArpRequest() {
266 return arpRequest;
267 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700268 }
269
270 @Override
271 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
272 Collection<Class<? extends IFloodlightService>> l =
273 new ArrayList<Class<? extends IFloodlightService>>();
274 l.add(IProxyArpService.class);
275 return l;
276 }
277
278 @Override
279 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
280 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
281 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
282 m.put(IProxyArpService.class, this);
283 return m;
284 }
285
286 @Override
287 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
288 Collection<Class<? extends IFloodlightService>> dependencies =
289 new ArrayList<Class<? extends IFloodlightService>>();
290 dependencies.add(IFloodlightProviderService.class);
291 dependencies.add(IRestApiService.class);
292 dependencies.add(IDatagridService.class);
293 dependencies.add(IConfigInfoService.class);
294 dependencies.add(IFlowPusherService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700295 dependencies.add(INetworkGraphService.class);
296 dependencies.add(IOnosDeviceService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700297 return dependencies;
298 }
299
300 @Override
301 public void init(FloodlightModuleContext context) {
TeruU7feef8a2014-04-03 00:15:49 -0700302 this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700303 this.configService = context.getServiceImpl(IConfigInfoService.class);
304 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700305 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700306 this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700307 this.networkGraphService = context.getServiceImpl(INetworkGraphService.class);
308 this.onosDeviceService = context.getServiceImpl(IOnosDeviceService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700309
310 // arpCache = new ArpCache();
311
312 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
313 .<InetAddress, ArpRequest>create());
314
315 }
316
317 @Override
318 public void startUp(FloodlightModuleContext context) {
319 this.vlan = configService.getVlan();
320 log.info("vlan set to {}", this.vlan);
321
322 restApi.addRestletRoutable(new ArpWebRoutable());
323 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
TeruU7feef8a2014-04-03 00:15:49 -0700324 networkGraph = networkGraphService.getNetworkGraph();
325
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700326 //
327 // Event notification setup: channels and event handlers
TeruU7feef8a2014-04-03 00:15:49 -0700328 //
329 broadcastPacketOutEventChannel = datagrid.addListener(BROADCAST_PACKET_OUT_CHANNEL_NAME,
330 broadcastPacketOutEventHandler,
331 Long.class,
332 BroadcastPacketOutNotification.class);
333
334 singlePacketOutEventChannel = datagrid.addListener(SINGLE_PACKET_OUT_CHANNEL_NAME,
335 singlePacketOutEventHandler,
336 Long.class,
337 SinglePacketOutNotification.class);
338
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700339 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
340 arpReplyEventHandler,
TeruU7feef8a2014-04-03 00:15:49 -0700341 Long.class,
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700342 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700343
344 Timer arpTimer = new Timer("arp-processing");
345 arpTimer.scheduleAtFixedRate(new TimerTask() {
346 @Override
347 public void run() {
348 doPeriodicArpProcessing();
349 }
350 }, 0, ARP_TIMER_PERIOD);
351 }
352
353 /*
354 * Function that runs periodically to manage the asynchronous request mechanism.
355 * It basically cleans up old ARP requests if we don't get a response for them.
356 * The caller can designate that a request should be retried indefinitely, and
357 * this task will handle that as well.
358 */
359 private void doPeriodicArpProcessing() {
360 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
361 .<InetAddress, ArpRequest>create();
362
363 // Have to synchronize externally on the Multimap while using an
364 // iterator,
365 // even though it's a synchronizedMultimap
366 synchronized (arpRequests) {
367 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
368 .entries().iterator();
369
370 while (it.hasNext()) {
371 Map.Entry<InetAddress, ArpRequest> entry = it.next();
372 ArpRequest request = entry.getValue();
373 if (request.isExpired()) {
374 log.debug("Cleaning expired ARP request for {}", entry
375 .getKey().getHostAddress());
376
TeruU7feef8a2014-04-03 00:15:49 -0700377 // If the ARP request is expired and then delete the device
378 // TODO check whether this is OK from this thread
379 HostArpRequester requester = (HostArpRequester) request.requester;
380 ARP req = requester.getArpRequest();
381 Device targetDev = networkGraph.getDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
382 if(targetDev != null) {
383 onosDeviceService.deleteOnosDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
384 if (log.isDebugEnabled()) {
385 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDev.getMacAddress());
386 }
387 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700388
389 it.remove();
390
391 if (request.shouldRetry()) {
392 retryList.put(entry.getKey(), request);
393 }
394 }
395 }
396 }
397
398 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
399 .asMap().entrySet()) {
400
401 InetAddress address = entry.getKey();
402
403 log.debug("Resending ARP request for {}", address.getHostAddress());
404
405 // Only ARP requests sent by the controller will have the retry flag
406 // set, so for now we can just send a new ARP request for that
407 // address.
408 sendArpRequestForAddress(address);
409
410 for (ArpRequest request : entry.getValue()) {
411 arpRequests.put(address, new ArpRequest(request));
412 }
413 }
414 }
415
416 @Override
417 public String getName() {
418 return "proxyarpmanager";
419 }
420
421 @Override
422 public boolean isCallbackOrderingPrereq(OFType type, String name) {
423 if (type == OFType.PACKET_IN) {
424 return "devicemanager".equals(name)
425 || "onosdevicemanager".equals(name);
426 } else {
427 return false;
428 }
429 }
430
431 @Override
432 public boolean isCallbackOrderingPostreq(OFType type, String name) {
433 return type == OFType.PACKET_IN && "onosforwarding".equals(name);
434 }
435
436 @Override
437 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
438
439 OFPacketIn pi = (OFPacketIn) msg;
440
441 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200442 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200443
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700444 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
445 ARP arp = (ARP) eth.getPayload();
446 if (arp.getOpCode() == ARP.OP_REQUEST) {
447 handleArpRequest(sw, pi, arp, eth);
448 } else if (arp.getOpCode() == ARP.OP_REPLY) {
449 // For replies we simply send a notification via Hazelcast
450 sendArpReplyNotification(eth, pi);
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200451
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700452 // handleArpReply(sw, pi, arp);
453 }
pingping-linb8757bf2013-12-13 01:48:58 +0800454
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700455 // Stop ARP packets here
456 return Command.STOP;
457 }
pingping-linb8757bf2013-12-13 01:48:58 +0800458
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700459 // Propagate everything else
460 return Command.CONTINUE;
461 }
pingping-linb8757bf2013-12-13 01:48:58 +0800462
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700463 private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
464 Ethernet eth) {
465 if (log.isTraceEnabled()) {
466 log.trace("ARP request received for {}",
467 inetAddressToString(arp.getTargetProtocolAddress()));
468 }
pingping-linb8757bf2013-12-13 01:48:58 +0800469
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700470 InetAddress target;
471 try {
472 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
473 } catch (UnknownHostException e) {
474 log.debug("Invalid address in ARP request", e);
475 return;
476 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700477
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700478 if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
479 // If the request came from outside our network, we only care if
480 // it was a request for one of our interfaces.
481 if (configService.isInterfaceAddress(target)) {
482 log.trace(
483 "ARP request for our interface. Sending reply {} => {}",
484 target.getHostAddress(),
485 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800486
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700487 sendArpReply(arp, sw.getId(), pi.getInPort(),
488 configService.getRouterMacAddress());
489 }
pingping-linb8757bf2013-12-13 01:48:58 +0800490
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700491 return;
492 }
pingping-linb8757bf2013-12-13 01:48:58 +0800493
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700494 // MACAddress macAddress = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800495
TeruU7feef8a2014-04-03 00:15:49 -0700496 arpRequests.put(target, new ArpRequest(
497 new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
498
499 Device targetDevice = networkGraph.getDeviceByMac(MACAddress.valueOf(arp.getTargetHardwareAddress()));
pingping-linb8757bf2013-12-13 01:48:58 +0800500
TeruU7feef8a2014-04-03 00:15:49 -0700501 if (targetDevice == null) {
502 if (log.isTraceEnabled()) {
503 log.trace("No device info found for {} - broadcasting",
504 target.getHostAddress());
505 }
506
507 // We don't know the device so broadcast the request out
508 BroadcastPacketOutNotification key =
509 new BroadcastPacketOutNotification(eth.serialize(),
510 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort());
511 log.debug("broadcastPacketOutEventChannel mac {}, ip {}, dpid {}, port {}, paket {}", eth.getSourceMAC().toLong(),
512 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort(), eth.serialize().length);
513 broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
514 }
515 else {
516 // Even if the device exists in our database, we do not reply to
517 // the request directly, but check whether the device is still valid
518 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800519
TeruU7feef8a2014-04-03 00:15:49 -0700520 if (log.isTraceEnabled()) {
521 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
522 new Object [] {
523 inetAddressToString(arp.getTargetProtocolAddress()),
524 macAddress,
525 HexString.toHexString(sw.getId()), pi.getInPort()});
526 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800527
TeruU7feef8a2014-04-03 00:15:49 -0700528 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200529
TeruU7feef8a2014-04-03 00:15:49 -0700530 Iterable<net.onrc.onos.ofcontroller.networkgraph.Port> outPorts = targetDevice.getAttachmentPoints();
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200531
TeruU7feef8a2014-04-03 00:15:49 -0700532 if (!outPorts.iterator().hasNext()){
533 if (log.isTraceEnabled()) {
534 log.trace("Device {} exists but is not connected to any ports" +
535 " - broadcasting", macAddress);
536 }
537
538// BroadcastPacketOutNotification key =
539// new BroadcastPacketOutNotification(eth.serialize(),
540// target, sw.getId(), pi.getInPort());
541// broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
542 }
543 else {
544 for (net.onrc.onos.ofcontroller.networkgraph.Port portObject : outPorts) {
545 //long outSwitch = 0;
546 //short outPort = 0;
Jonathan Hart7804bea2014-01-07 10:50:52 -0800547
TeruU7feef8a2014-04-03 00:15:49 -0700548 if(portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
549 continue;
550 }
551
552 short outPort = portObject.getNumber().shortValue();
553 Switch outSwitchObject = portObject.getSwitch();
554 long outSwitch = outSwitchObject.getDpid();
555
556 if (log.isTraceEnabled()) {
557 log.trace("Probing device {} on port {}/{}",
558 new Object[] {macAddress,
559 HexString.toHexString(outSwitch), outPort});
560 }
561
562 SinglePacketOutNotification key =
563 new SinglePacketOutNotification(eth.serialize(),
564 ByteBuffer.wrap(target.getAddress()).getInt(), outSwitch, outPort);
565 singlePacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
566 }
567 }
568 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700569 }
pingping-linb8757bf2013-12-13 01:48:58 +0800570
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700571 // Not used because device manager currently updates the database
572 // for ARP replies. May be useful in the future.
573 private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp) {
574 if (log.isTraceEnabled()) {
575 log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
576 inetAddressToString(arp.getSenderProtocolAddress()),
577 HexString.toHexString(arp.getSenderHardwareAddress()),
578 HexString.toHexString(sw.getId()), pi.getInPort()});
579 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800580
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700581 InetAddress senderIpAddress;
582 try {
583 senderIpAddress = InetAddress.getByAddress(arp
584 .getSenderProtocolAddress());
585 } catch (UnknownHostException e) {
586 log.debug("Invalid address in ARP reply", e);
587 return;
588 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800589
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700590 MACAddress senderMacAddress = MACAddress.valueOf(arp
591 .getSenderHardwareAddress());
592
593 // See if anyone's waiting for this ARP reply
594 Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
595
596 // Synchronize on the Multimap while using an iterator for one of the
597 // sets
598 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
599 requests.size());
600 synchronized (arpRequests) {
601 Iterator<ArpRequest> it = requests.iterator();
602 while (it.hasNext()) {
603 ArpRequest request = it.next();
604 it.remove();
605 requestsToSend.add(request);
606 }
607 }
608
609 // Don't hold an ARP lock while dispatching requests
610 for (ArpRequest request : requestsToSend) {
611 request.dispatchReply(senderIpAddress, senderMacAddress);
612 }
613 }
614
615 private void sendArpRequestForAddress(InetAddress ipAddress) {
616 // TODO what should the sender IP address and MAC address be if no
617 // IP addresses are configured? Will there ever be a need to send
618 // ARP requests from the controller in that case?
619 // All-zero MAC address doesn't seem to work - hosts don't respond to it
620
621 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
622 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
623 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
624 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
625 (byte) 0xff, (byte) 0xff, (byte) 0xff};
626
627 ARP arpRequest = new ARP();
628
629 arpRequest
630 .setHardwareType(ARP.HW_TYPE_ETHERNET)
631 .setProtocolType(ARP.PROTO_TYPE_IP)
632 .setHardwareAddressLength(
633 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
634 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
635 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
636 .setTargetProtocolAddress(ipAddress.getAddress());
637
638 MACAddress routerMacAddress = configService.getRouterMacAddress();
639 // TODO hack for now as it's unclear what the MAC address should be
640 byte[] senderMacAddress = genericNonZeroMac;
641 if (routerMacAddress != null) {
642 senderMacAddress = routerMacAddress.toBytes();
643 }
644 arpRequest.setSenderHardwareAddress(senderMacAddress);
645
646 byte[] senderIPAddress = zeroIpv4;
647 Interface intf = configService.getOutgoingInterface(ipAddress);
648 if (intf != null) {
649 senderIPAddress = intf.getIpAddress().getAddress();
650 }
651
652 arpRequest.setSenderProtocolAddress(senderIPAddress);
653
654 Ethernet eth = new Ethernet();
655 eth.setSourceMACAddress(senderMacAddress)
656 .setDestinationMACAddress(broadcastMac)
657 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
658
659 if (vlan != NO_VLAN) {
660 eth.setVlanID(vlan).setPriorityCode((byte) 0);
661 }
662
663 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU7feef8a2014-04-03 00:15:49 -0700664 SinglePacketOutNotification key =
665 new SinglePacketOutNotification(eth.serialize(), ByteBuffer.wrap(ipAddress.getAddress()).getInt(),
666 intf.getDpid(), intf.getPort());
667 singlePacketOutEventChannel.addTransientEntry(MACAddress.valueOf(senderMacAddress).toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700668 }
TeruU7feef8a2014-04-03 00:15:49 -0700669
670 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
671 sendArpRequestToSwitches(dstAddress, arpRequest, 0,
672 OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700673 }
674
675 private void sendArpRequestToSwitches(InetAddress dstAddress,
676 byte[] arpRequest, long inSwitch, short inPort) {
677
678 if (configService.hasLayer3Configuration()) {
679 Interface intf = configService.getOutgoingInterface(dstAddress);
680 if (intf == null) {
681 // TODO here it should be broadcast out all non-interface edge
682 // ports.
683 // I think we can assume that if it's not a request for an
684 // external
685 // network, it's an ARP for a host in our own network. So we
686 // want to
687 // send it out all edge ports that don't have an interface
688 // configured
689 // to ensure it reaches all hosts in our network.
690 log.debug("No interface found to send ARP request for {}",
691 dstAddress.getHostAddress());
692 } else {
693 sendArpRequestOutPort(arpRequest, intf.getDpid(),
694 intf.getPort());
695 }
696 } else {
697 // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
698 broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
699 }
700 }
701
702 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
703 ARP arp = (ARP) eth.getPayload();
704
705 if (log.isTraceEnabled()) {
706 log.trace("Sending ARP reply for {} to other ONOS instances",
707 inetAddressToString(arp.getSenderProtocolAddress()));
708 }
709
710 InetAddress targetAddress;
711
712 try {
713 targetAddress = InetAddress.getByAddress(arp
714 .getSenderProtocolAddress());
715 } catch (UnknownHostException e) {
716 log.error("Unknown host", e);
717 return;
718 }
719
720 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
721
TeruU7feef8a2014-04-03 00:15:49 -0700722 ArpReplyNotification key =
723 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
724 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
725 arpReplyEventChannel.addTransientEntry(mac.toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700726 }
727
728 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
729 short inPort) {
730 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
731
732 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
733
734 OFPacketOut po = new OFPacketOut();
735 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
736 .setPacketData(arpRequest);
737
738 List<OFAction> actions = new ArrayList<OFAction>();
739
TeruU7feef8a2014-04-03 00:15:49 -0700740 Switch graphSw = networkGraph.getSwitch(sw.getId());
741 Collection<net.onrc.onos.ofcontroller.networkgraph.Port> ports = graphSw.getPorts();
742
743 if (ports == null) {
744 continue;
745 }
746
747 for (net.onrc.onos.ofcontroller.networkgraph.Port portObject : ports) {
748 if (portObject.getOutgoingLink() == null && portObject.getNumber() > 0) {
749 Long portNumber = portObject.getNumber();
750
751 if (sw.getId() == inSwitch && portNumber.shortValue() == inPort) {
752 // This is the port that the ARP message came in,
753 // so don't broadcast out this port
754 continue;
755 }
756 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
757 new net.onrc.onos.ofcontroller.util.Port(portNumber.shortValue())));
758 actions.add(new OFActionOutput(portNumber.shortValue()));
759 }
760 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700761
762 po.setActions(actions);
763 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
764 po.setActionsLength(actionsLength);
765 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
766 + arpRequest.length);
767
768 flowPusher.add(sw, po);
769 }
770
771 if (log.isTraceEnabled()) {
772 log.trace("Broadcast ARP request to: {}", switchPorts);
773 }
774 }
775
776 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
777 if (log.isTraceEnabled()) {
778 log.trace("Sending ARP request out {}/{}",
779 HexString.toHexString(dpid), port);
780 }
781
782 OFPacketOut po = new OFPacketOut();
783 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
784 .setPacketData(arpRequest);
785
786 List<OFAction> actions = new ArrayList<OFAction>();
787 actions.add(new OFActionOutput(port));
788 po.setActions(actions);
789 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
790 po.setActionsLength(actionsLength);
791 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
792 + arpRequest.length);
793
794 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
795
796 if (sw == null) {
797 log.warn("Switch not found when sending ARP request");
798 return;
799 }
800
801 flowPusher.add(sw, po);
802 }
803
804 private void sendArpReply(ARP arpRequest, long dpid, short port,
805 MACAddress targetMac) {
806 if (log.isTraceEnabled()) {
807 log.trace(
808 "Sending reply {} => {} to {}",
809 new Object[] {
810 inetAddressToString(arpRequest
811 .getTargetProtocolAddress()),
812 targetMac,
813 inetAddressToString(arpRequest
814 .getSenderProtocolAddress())});
815 }
816
817 ARP arpReply = new ARP();
818 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
819 .setProtocolType(ARP.PROTO_TYPE_IP)
820 .setHardwareAddressLength(
821 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
822 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
823 .setOpCode(ARP.OP_REPLY)
824 .setSenderHardwareAddress(targetMac.toBytes())
825 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
826 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
827 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
828
829 Ethernet eth = new Ethernet();
830 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
831 .setSourceMACAddress(targetMac.toBytes())
832 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
833
834 if (vlan != NO_VLAN) {
835 eth.setVlanID(vlan).setPriorityCode((byte) 0);
836 }
837
838 List<OFAction> actions = new ArrayList<OFAction>();
839 actions.add(new OFActionOutput(port));
840
841 OFPacketOut po = new OFPacketOut();
842 po.setInPort(OFPort.OFPP_NONE)
843 .setBufferId(-1)
844 .setPacketData(eth.serialize())
845 .setActions(actions)
846 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
847 .setLengthU(
848 OFPacketOut.MINIMUM_LENGTH
849 + OFActionOutput.MINIMUM_LENGTH
850 + po.getPacketData().length);
851
852 List<OFMessage> msgList = new ArrayList<OFMessage>();
853 msgList.add(po);
854
855 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
856
857 if (sw == null) {
858 log.warn("Switch {} not found when sending ARP reply",
859 HexString.toHexString(dpid));
860 return;
861 }
862
863 flowPusher.add(sw, po);
864 }
865
866 private String inetAddressToString(byte[] bytes) {
867 try {
868 return InetAddress.getByAddress(bytes).getHostAddress();
869 } catch (UnknownHostException e) {
870 log.debug("Invalid IP address", e);
871 return "";
872 }
873 }
874
875 /*
876 * IProxyArpService methods
877 */
878
879 @Override
880 public MACAddress getMacAddress(InetAddress ipAddress) {
881 // return arpCache.lookup(ipAddress);
882 return null;
883 }
884
885 @Override
886 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
887 boolean retry) {
888 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
889
890 // Sanity check to make sure we don't send a request for our own address
891 if (!configService.isInterfaceAddress(ipAddress)) {
892 sendArpRequestForAddress(ipAddress);
893 }
894 }
895
896 @Override
897 public List<String> getMappings() {
898 return new ArrayList<String>();
899 }
900
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700901 private void sendArpReplyToWaitingRequesters(InetAddress address,
902 MACAddress mac) {
903 log.debug("Sending ARP reply for {} to requesters",
904 address.getHostAddress());
905
906 // See if anyone's waiting for this ARP reply
907 Set<ArpRequest> requests = arpRequests.get(address);
908
909 // Synchronize on the Multimap while using an iterator for one of the
910 // sets
911 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
912 requests.size());
913 synchronized (arpRequests) {
914 Iterator<ArpRequest> it = requests.iterator();
915 while (it.hasNext()) {
916 ArpRequest request = it.next();
917 it.remove();
918 requestsToSend.add(request);
919 }
920 }
921
TeruU7feef8a2014-04-03 00:15:49 -0700922 //TODO here, comment outed from long time ago. I will check if we need it later.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700923 /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
924 InetAddresses.coerceToInteger(address));
925
926 MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
927
928 log.debug("Found {} at {} in network map",
929 address.getHostAddress(), mac);*/
930
931 // Don't hold an ARP lock while dispatching requests
932 for (ArpRequest request : requestsToSend) {
933 request.dispatchReply(address, mac);
934 }
935 }
TeruU7feef8a2014-04-03 00:15:49 -0700936}