blob: f876310e9a04a14b371f7948deea62096e3b7cf9 [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 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 Hart472062d2014-04-03 10:56:48 -070035import net.onrc.onos.core.topology.Device;
36import net.onrc.onos.core.topology.INetworkGraphService;
37import net.onrc.onos.core.topology.NetworkGraph;
38import net.onrc.onos.core.topology.Switch;
Jonathan Hart23701d12014-04-03 10:45:48 -070039import net.onrc.onos.core.util.Dpid;
Jonathan Hart23701d12014-04-03 10:45:48 -070040import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120041
42import org.openflow.protocol.OFMessage;
43import org.openflow.protocol.OFPacketIn;
44import org.openflow.protocol.OFPacketOut;
45import org.openflow.protocol.OFPort;
46import org.openflow.protocol.OFType;
47import org.openflow.protocol.action.OFAction;
48import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120049import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120050import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
Jonathan Hart4dfc3652013-08-02 20:22:36 +120053import com.google.common.collect.HashMultimap;
54import com.google.common.collect.Multimaps;
55import com.google.common.collect.SetMultimap;
56
Jonathan Hart18ad55c2013-11-11 22:49:55 -080057public class ProxyArpManager implements IProxyArpService, IOFMessageListener,
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070058 IFloodlightModule {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070059 private static final Logger log = LoggerFactory
60 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080061
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070062 private static final long ARP_TIMER_PERIOD = 100; // ms
Jonathan Hartdf6ec332013-08-04 01:37:14 +120063
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070064 private static final int ARP_REQUEST_TIMEOUT = 2000; // ms
Jonathan Hartda4d0e12013-09-30 21:00:20 +130065
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070066 private IFloodlightProviderService floodlightProvider;
67 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070068 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
69 private IEventChannel<Long, BroadcastPacketOutNotification> broadcastPacketOutEventChannel;
70 private IEventChannel<Long, SinglePacketOutNotification> singlePacketOutEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070071 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU7feef8a2014-04-03 00:15:49 -070072 private static final String BROADCAST_PACKET_OUT_CHANNEL_NAME = "onos.broadcast_packet_out";
73 private static final String SINGLE_PACKET_OUT_CHANNEL_NAME = "onos.single_packet_out";
74 private ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
75 private BroadcastPacketOutEventHandler broadcastPacketOutEventHandler = new BroadcastPacketOutEventHandler();
76 private SinglePacketOutEventHandler singlePacketOutEventHandler = new SinglePacketOutEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070077
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070078 private IConfigInfoService configService;
79 private IRestApiService restApi;
80 private IFlowPusherService flowPusher;
TeruU7feef8a2014-04-03 00:15:49 -070081
82 private INetworkGraphService networkGraphService;
83 private NetworkGraph networkGraph;
84 private IOnosDeviceService onosDeviceService;
Jonathan Harte93aed42013-12-05 18:39:50 -080085
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070086 private short vlan;
87 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080088
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070089 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120090
TeruU7feef8a2014-04-03 00:15:49 -070091 private class BroadcastPacketOutEventHandler implements
92 IEventChannelListener<Long, BroadcastPacketOutNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070093
TeruU7feef8a2014-04-03 00:15:49 -070094 @Override
95 public void entryAdded(BroadcastPacketOutNotification value) {
96 if(log.isTraceEnabled()) {
97 log.trace("entryAdded ip{}, sw {}, port {}, packet {}", value.getTargetAddress(), value.getInSwitch(), value.getInPort(), value.packet.length);
98 }
99 BroadcastPacketOutNotification notification = (BroadcastPacketOutNotification) value;
100 broadcastArpRequestOutMyEdge(notification.packet,
101 notification.getInSwitch(),
102 notification.getInPort());
103
104 // set timestamp
105 ByteBuffer buffer = ByteBuffer.allocate(4);
106 buffer.putInt(notification.getTargetAddress());
107 InetAddress addr = null;
108 try {
109 addr = InetAddress.getByAddress(buffer.array());
110 } catch (UnknownHostException e) {
111 log.error("Exception:", e);
112 }
113
114 if (addr != null) {
115 for (ArpRequest request : arpRequests.get(addr)) {
116 request.setRequestTime();
117 }
118 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700119 }
TeruU7feef8a2014-04-03 00:15:49 -0700120
121 @Override
122 public void entryUpdated(BroadcastPacketOutNotification value) {
123 log.debug("entryUpdated");
124 // TODO: For now, entryUpdated() is processed as entryAdded()
125 entryAdded(value);
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700126 }
TeruU7feef8a2014-04-03 00:15:49 -0700127
128 @Override
129 public void entryRemoved(BroadcastPacketOutNotification value) {
130 log.debug("entryRemoved");
131 // TODO: Not implemented. Revisit when this module is refactored
132 }
133 }
134
135 private class SinglePacketOutEventHandler implements
136 IEventChannelListener<Long, SinglePacketOutNotification> {
137 @Override
138 public void entryAdded(SinglePacketOutNotification packetOutNotification) {
139 log.debug("entryAdded");
140 SinglePacketOutNotification notification =
141 (SinglePacketOutNotification) packetOutNotification;
142 sendArpRequestOutPort(notification.packet,
143 notification.getOutSwitch(),
144 notification.getOutPort());
145
146 // set timestamp
147 ByteBuffer buffer = ByteBuffer.allocate(4);
148 buffer.putInt(notification.getTargetAddress());
149 InetAddress addr = null;
150 try {
151 addr = InetAddress.getByAddress(buffer.array());
152 } catch (UnknownHostException e) {
153 log.error("Exception:", e);
154 }
155
156 if (addr != null) {
157 for (ArpRequest request : arpRequests.get(addr)) {
158 request.setRequestTime();
159 }
160 }
161 }
162
163 @Override
164 public void entryUpdated(SinglePacketOutNotification packetOutNotification) {
165 log.debug("entryUpdated");
166 // TODO: For now, entryUpdated() is processed as entryAdded()
167 entryAdded(packetOutNotification);
168 }
169
170 @Override
171 public void entryRemoved(SinglePacketOutNotification packetOutNotification) {
172 log.debug("entryRemoved");
173 // TODO: Not implemented. Revisit when this module is refactored
174 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700175 }
176
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700177 private class ArpReplyEventHandler implements
TeruU7feef8a2014-04-03 00:15:49 -0700178 IEventChannelListener<Long, ArpReplyNotification> {
179
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700180 @Override
181 public void entryAdded(ArpReplyNotification arpReply) {
TeruU7feef8a2014-04-03 00:15:49 -0700182 log.debug("Received ARP reply notification for ip {}, mac {}",
183 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
184 ByteBuffer buffer = ByteBuffer.allocate(4);
185 buffer.putInt(arpReply.getTargetAddress());
186 InetAddress addr = null;
187 try {
188 addr = InetAddress.getByAddress(buffer.array());
189 } catch (UnknownHostException e) {
190 log.error("Exception:", e);
191 }
192
193 if(addr != null) {
194 sendArpReplyToWaitingRequesters(addr,
195 arpReply.getTargetMacAddress());
196 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700197 }
198
199 @Override
200 public void entryUpdated(ArpReplyNotification arpReply) {
201 // TODO: For now, entryUpdated() is processed as entryAdded()
202 entryAdded(arpReply);
203 }
204
205 @Override
206 public void entryRemoved(ArpReplyNotification arpReply) {
207 // TODO: Not implemented. Revisit when this module is refactored
208 }
209 }
210
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700211 private static class ArpRequest {
212 private final IArpRequester requester;
213 private final boolean retry;
214 private boolean sent = false;
215 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200216
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700217 public ArpRequest(IArpRequester requester, boolean retry) {
218 this.requester = requester;
219 this.retry = retry;
220 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200221
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700222 public ArpRequest(ArpRequest old) {
223 this.requester = old.requester;
224 this.retry = old.retry;
225 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200226
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700227 public boolean isExpired() {
228 return sent
229 && ((System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT);
230 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200231
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700232 public boolean shouldRetry() {
233 return retry;
234 }
235
236 public void dispatchReply(InetAddress ipAddress,
237 MACAddress replyMacAddress) {
238 requester.arpResponse(ipAddress, replyMacAddress);
239 }
240
241 public void setRequestTime() {
242 this.requestTime = System.currentTimeMillis();
243 this.sent = true;
244 }
245 }
246
247 private class HostArpRequester implements IArpRequester {
248 private final ARP arpRequest;
249 private final long dpid;
250 private final short port;
251
252 public HostArpRequester(ARP arpRequest, long dpid, short port) {
253 this.arpRequest = arpRequest;
254 this.dpid = dpid;
255 this.port = port;
256 }
257
258 @Override
259 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
260 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
261 macAddress);
262 }
TeruU7feef8a2014-04-03 00:15:49 -0700263
264 public ARP getArpRequest() {
265 return arpRequest;
266 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700267 }
268
269 @Override
270 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
271 Collection<Class<? extends IFloodlightService>> l =
272 new ArrayList<Class<? extends IFloodlightService>>();
273 l.add(IProxyArpService.class);
274 return l;
275 }
276
277 @Override
278 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
279 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
280 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
281 m.put(IProxyArpService.class, this);
282 return m;
283 }
284
285 @Override
286 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
287 Collection<Class<? extends IFloodlightService>> dependencies =
288 new ArrayList<Class<? extends IFloodlightService>>();
289 dependencies.add(IFloodlightProviderService.class);
290 dependencies.add(IRestApiService.class);
291 dependencies.add(IDatagridService.class);
292 dependencies.add(IConfigInfoService.class);
293 dependencies.add(IFlowPusherService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700294 dependencies.add(INetworkGraphService.class);
295 dependencies.add(IOnosDeviceService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700296 return dependencies;
297 }
298
299 @Override
300 public void init(FloodlightModuleContext context) {
TeruU7feef8a2014-04-03 00:15:49 -0700301 this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700302 this.configService = context.getServiceImpl(IConfigInfoService.class);
303 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700304 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700305 this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700306 this.networkGraphService = context.getServiceImpl(INetworkGraphService.class);
307 this.onosDeviceService = context.getServiceImpl(IOnosDeviceService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700308
309 // arpCache = new ArpCache();
310
311 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
312 .<InetAddress, ArpRequest>create());
313
314 }
315
316 @Override
317 public void startUp(FloodlightModuleContext context) {
318 this.vlan = configService.getVlan();
319 log.info("vlan set to {}", this.vlan);
320
321 restApi.addRestletRoutable(new ArpWebRoutable());
322 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
TeruU7feef8a2014-04-03 00:15:49 -0700323 networkGraph = networkGraphService.getNetworkGraph();
324
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700325 //
326 // Event notification setup: channels and event handlers
TeruU7feef8a2014-04-03 00:15:49 -0700327 //
328 broadcastPacketOutEventChannel = datagrid.addListener(BROADCAST_PACKET_OUT_CHANNEL_NAME,
329 broadcastPacketOutEventHandler,
330 Long.class,
331 BroadcastPacketOutNotification.class);
332
333 singlePacketOutEventChannel = datagrid.addListener(SINGLE_PACKET_OUT_CHANNEL_NAME,
334 singlePacketOutEventHandler,
335 Long.class,
336 SinglePacketOutNotification.class);
337
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700338 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
339 arpReplyEventHandler,
TeruU7feef8a2014-04-03 00:15:49 -0700340 Long.class,
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700341 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700342
343 Timer arpTimer = new Timer("arp-processing");
344 arpTimer.scheduleAtFixedRate(new TimerTask() {
345 @Override
346 public void run() {
347 doPeriodicArpProcessing();
348 }
349 }, 0, ARP_TIMER_PERIOD);
350 }
351
352 /*
353 * Function that runs periodically to manage the asynchronous request mechanism.
354 * It basically cleans up old ARP requests if we don't get a response for them.
355 * The caller can designate that a request should be retried indefinitely, and
356 * this task will handle that as well.
357 */
358 private void doPeriodicArpProcessing() {
359 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
360 .<InetAddress, ArpRequest>create();
361
362 // Have to synchronize externally on the Multimap while using an
363 // iterator,
364 // even though it's a synchronizedMultimap
365 synchronized (arpRequests) {
366 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
367 .entries().iterator();
368
369 while (it.hasNext()) {
370 Map.Entry<InetAddress, ArpRequest> entry = it.next();
371 ArpRequest request = entry.getValue();
372 if (request.isExpired()) {
373 log.debug("Cleaning expired ARP request for {}", entry
374 .getKey().getHostAddress());
375
TeruU7feef8a2014-04-03 00:15:49 -0700376 // If the ARP request is expired and then delete the device
377 // TODO check whether this is OK from this thread
378 HostArpRequester requester = (HostArpRequester) request.requester;
379 ARP req = requester.getArpRequest();
380 Device targetDev = networkGraph.getDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
381 if(targetDev != null) {
382 onosDeviceService.deleteOnosDeviceByMac(MACAddress.valueOf(req.getTargetHardwareAddress()));
383 if (log.isDebugEnabled()) {
384 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDev.getMacAddress());
385 }
386 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700387
388 it.remove();
389
390 if (request.shouldRetry()) {
391 retryList.put(entry.getKey(), request);
392 }
393 }
394 }
395 }
396
397 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
398 .asMap().entrySet()) {
399
400 InetAddress address = entry.getKey();
401
402 log.debug("Resending ARP request for {}", address.getHostAddress());
403
404 // Only ARP requests sent by the controller will have the retry flag
405 // set, so for now we can just send a new ARP request for that
406 // address.
407 sendArpRequestForAddress(address);
408
409 for (ArpRequest request : entry.getValue()) {
410 arpRequests.put(address, new ArpRequest(request));
411 }
412 }
413 }
414
415 @Override
416 public String getName() {
417 return "proxyarpmanager";
418 }
419
420 @Override
421 public boolean isCallbackOrderingPrereq(OFType type, String name) {
422 if (type == OFType.PACKET_IN) {
423 return "devicemanager".equals(name)
424 || "onosdevicemanager".equals(name);
425 } else {
426 return false;
427 }
428 }
429
430 @Override
431 public boolean isCallbackOrderingPostreq(OFType type, String name) {
432 return type == OFType.PACKET_IN && "onosforwarding".equals(name);
433 }
434
435 @Override
436 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
437
438 OFPacketIn pi = (OFPacketIn) msg;
439
440 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200441 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200442
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700443 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
444 ARP arp = (ARP) eth.getPayload();
445 if (arp.getOpCode() == ARP.OP_REQUEST) {
446 handleArpRequest(sw, pi, arp, eth);
447 } else if (arp.getOpCode() == ARP.OP_REPLY) {
448 // For replies we simply send a notification via Hazelcast
449 sendArpReplyNotification(eth, pi);
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200450
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700451 // handleArpReply(sw, pi, arp);
452 }
pingping-linb8757bf2013-12-13 01:48:58 +0800453
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700454 // Stop ARP packets here
455 return Command.STOP;
456 }
pingping-linb8757bf2013-12-13 01:48:58 +0800457
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700458 // Propagate everything else
459 return Command.CONTINUE;
460 }
pingping-linb8757bf2013-12-13 01:48:58 +0800461
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700462 private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
463 Ethernet eth) {
464 if (log.isTraceEnabled()) {
465 log.trace("ARP request received for {}",
466 inetAddressToString(arp.getTargetProtocolAddress()));
467 }
pingping-linb8757bf2013-12-13 01:48:58 +0800468
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700469 InetAddress target;
470 try {
471 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
472 } catch (UnknownHostException e) {
473 log.debug("Invalid address in ARP request", e);
474 return;
475 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700476
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700477 if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
478 // If the request came from outside our network, we only care if
479 // it was a request for one of our interfaces.
480 if (configService.isInterfaceAddress(target)) {
481 log.trace(
482 "ARP request for our interface. Sending reply {} => {}",
483 target.getHostAddress(),
484 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800485
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700486 sendArpReply(arp, sw.getId(), pi.getInPort(),
487 configService.getRouterMacAddress());
488 }
pingping-linb8757bf2013-12-13 01:48:58 +0800489
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700490 return;
491 }
pingping-linb8757bf2013-12-13 01:48:58 +0800492
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700493 // MACAddress macAddress = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800494
TeruU7feef8a2014-04-03 00:15:49 -0700495 arpRequests.put(target, new ArpRequest(
496 new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
497
498 Device targetDevice = networkGraph.getDeviceByMac(MACAddress.valueOf(arp.getTargetHardwareAddress()));
pingping-linb8757bf2013-12-13 01:48:58 +0800499
TeruU7feef8a2014-04-03 00:15:49 -0700500 if (targetDevice == null) {
501 if (log.isTraceEnabled()) {
502 log.trace("No device info found for {} - broadcasting",
503 target.getHostAddress());
504 }
505
506 // We don't know the device so broadcast the request out
507 BroadcastPacketOutNotification key =
508 new BroadcastPacketOutNotification(eth.serialize(),
509 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort());
510 log.debug("broadcastPacketOutEventChannel mac {}, ip {}, dpid {}, port {}, paket {}", eth.getSourceMAC().toLong(),
511 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort(), eth.serialize().length);
512 broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
513 }
514 else {
515 // Even if the device exists in our database, we do not reply to
516 // the request directly, but check whether the device is still valid
517 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800518
TeruU7feef8a2014-04-03 00:15:49 -0700519 if (log.isTraceEnabled()) {
520 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
521 new Object [] {
522 inetAddressToString(arp.getTargetProtocolAddress()),
523 macAddress,
524 HexString.toHexString(sw.getId()), pi.getInPort()});
525 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800526
TeruU7feef8a2014-04-03 00:15:49 -0700527 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200528
Jonathan Hart472062d2014-04-03 10:56:48 -0700529 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200530
TeruU7feef8a2014-04-03 00:15:49 -0700531 if (!outPorts.iterator().hasNext()){
532 if (log.isTraceEnabled()) {
533 log.trace("Device {} exists but is not connected to any ports" +
534 " - broadcasting", macAddress);
535 }
536
537// BroadcastPacketOutNotification key =
538// new BroadcastPacketOutNotification(eth.serialize(),
539// target, sw.getId(), pi.getInPort());
540// broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
541 }
542 else {
Jonathan Hart472062d2014-04-03 10:56:48 -0700543 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
TeruU7feef8a2014-04-03 00:15:49 -0700544 //long outSwitch = 0;
545 //short outPort = 0;
Jonathan Hart7804bea2014-01-07 10:50:52 -0800546
TeruU7feef8a2014-04-03 00:15:49 -0700547 if(portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
548 continue;
549 }
550
551 short outPort = portObject.getNumber().shortValue();
552 Switch outSwitchObject = portObject.getSwitch();
553 long outSwitch = outSwitchObject.getDpid();
554
555 if (log.isTraceEnabled()) {
556 log.trace("Probing device {} on port {}/{}",
557 new Object[] {macAddress,
558 HexString.toHexString(outSwitch), outPort});
559 }
560
561 SinglePacketOutNotification key =
562 new SinglePacketOutNotification(eth.serialize(),
563 ByteBuffer.wrap(target.getAddress()).getInt(), outSwitch, outPort);
564 singlePacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
565 }
566 }
567 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700568 }
pingping-linb8757bf2013-12-13 01:48:58 +0800569
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700570 // Not used because device manager currently updates the database
571 // for ARP replies. May be useful in the future.
572 private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp) {
573 if (log.isTraceEnabled()) {
574 log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
575 inetAddressToString(arp.getSenderProtocolAddress()),
576 HexString.toHexString(arp.getSenderHardwareAddress()),
577 HexString.toHexString(sw.getId()), pi.getInPort()});
578 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800579
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700580 InetAddress senderIpAddress;
581 try {
582 senderIpAddress = InetAddress.getByAddress(arp
583 .getSenderProtocolAddress());
584 } catch (UnknownHostException e) {
585 log.debug("Invalid address in ARP reply", e);
586 return;
587 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800588
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700589 MACAddress senderMacAddress = MACAddress.valueOf(arp
590 .getSenderHardwareAddress());
591
592 // See if anyone's waiting for this ARP reply
593 Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
594
595 // Synchronize on the Multimap while using an iterator for one of the
596 // sets
597 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
598 requests.size());
599 synchronized (arpRequests) {
600 Iterator<ArpRequest> it = requests.iterator();
601 while (it.hasNext()) {
602 ArpRequest request = it.next();
603 it.remove();
604 requestsToSend.add(request);
605 }
606 }
607
608 // Don't hold an ARP lock while dispatching requests
609 for (ArpRequest request : requestsToSend) {
610 request.dispatchReply(senderIpAddress, senderMacAddress);
611 }
612 }
613
614 private void sendArpRequestForAddress(InetAddress ipAddress) {
615 // TODO what should the sender IP address and MAC address be if no
616 // IP addresses are configured? Will there ever be a need to send
617 // ARP requests from the controller in that case?
618 // All-zero MAC address doesn't seem to work - hosts don't respond to it
619
620 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
621 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
622 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
623 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
624 (byte) 0xff, (byte) 0xff, (byte) 0xff};
625
626 ARP arpRequest = new ARP();
627
628 arpRequest
629 .setHardwareType(ARP.HW_TYPE_ETHERNET)
630 .setProtocolType(ARP.PROTO_TYPE_IP)
631 .setHardwareAddressLength(
632 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
633 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
634 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
635 .setTargetProtocolAddress(ipAddress.getAddress());
636
637 MACAddress routerMacAddress = configService.getRouterMacAddress();
638 // TODO hack for now as it's unclear what the MAC address should be
639 byte[] senderMacAddress = genericNonZeroMac;
640 if (routerMacAddress != null) {
641 senderMacAddress = routerMacAddress.toBytes();
642 }
643 arpRequest.setSenderHardwareAddress(senderMacAddress);
644
645 byte[] senderIPAddress = zeroIpv4;
646 Interface intf = configService.getOutgoingInterface(ipAddress);
647 if (intf != null) {
648 senderIPAddress = intf.getIpAddress().getAddress();
649 }
650
651 arpRequest.setSenderProtocolAddress(senderIPAddress);
652
653 Ethernet eth = new Ethernet();
654 eth.setSourceMACAddress(senderMacAddress)
655 .setDestinationMACAddress(broadcastMac)
656 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
657
658 if (vlan != NO_VLAN) {
659 eth.setVlanID(vlan).setPriorityCode((byte) 0);
660 }
661
662 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU7feef8a2014-04-03 00:15:49 -0700663 SinglePacketOutNotification key =
664 new SinglePacketOutNotification(eth.serialize(), ByteBuffer.wrap(ipAddress.getAddress()).getInt(),
665 intf.getDpid(), intf.getPort());
666 singlePacketOutEventChannel.addTransientEntry(MACAddress.valueOf(senderMacAddress).toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700667 }
TeruU7feef8a2014-04-03 00:15:49 -0700668
669 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
670 sendArpRequestToSwitches(dstAddress, arpRequest, 0,
671 OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700672 }
673
674 private void sendArpRequestToSwitches(InetAddress dstAddress,
675 byte[] arpRequest, long inSwitch, short inPort) {
676
677 if (configService.hasLayer3Configuration()) {
678 Interface intf = configService.getOutgoingInterface(dstAddress);
679 if (intf == null) {
680 // TODO here it should be broadcast out all non-interface edge
681 // ports.
682 // I think we can assume that if it's not a request for an
683 // external
684 // network, it's an ARP for a host in our own network. So we
685 // want to
686 // send it out all edge ports that don't have an interface
687 // configured
688 // to ensure it reaches all hosts in our network.
689 log.debug("No interface found to send ARP request for {}",
690 dstAddress.getHostAddress());
691 } else {
692 sendArpRequestOutPort(arpRequest, intf.getDpid(),
693 intf.getPort());
694 }
695 } else {
696 // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
697 broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
698 }
699 }
700
701 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
702 ARP arp = (ARP) eth.getPayload();
703
704 if (log.isTraceEnabled()) {
705 log.trace("Sending ARP reply for {} to other ONOS instances",
706 inetAddressToString(arp.getSenderProtocolAddress()));
707 }
708
709 InetAddress targetAddress;
710
711 try {
712 targetAddress = InetAddress.getByAddress(arp
713 .getSenderProtocolAddress());
714 } catch (UnknownHostException e) {
715 log.error("Unknown host", e);
716 return;
717 }
718
719 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
720
TeruU7feef8a2014-04-03 00:15:49 -0700721 ArpReplyNotification key =
722 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
723 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
724 arpReplyEventChannel.addTransientEntry(mac.toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700725 }
726
727 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
728 short inPort) {
729 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
730
731 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
732
733 OFPacketOut po = new OFPacketOut();
734 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
735 .setPacketData(arpRequest);
736
737 List<OFAction> actions = new ArrayList<OFAction>();
738
TeruU7feef8a2014-04-03 00:15:49 -0700739 Switch graphSw = networkGraph.getSwitch(sw.getId());
Jonathan Hart472062d2014-04-03 10:56:48 -0700740 Collection<net.onrc.onos.core.topology.Port> ports = graphSw.getPorts();
TeruU7feef8a2014-04-03 00:15:49 -0700741
742 if (ports == null) {
743 continue;
744 }
745
Jonathan Hart472062d2014-04-03 10:56:48 -0700746 for (net.onrc.onos.core.topology.Port portObject : ports) {
TeruU7feef8a2014-04-03 00:15:49 -0700747 if (portObject.getOutgoingLink() == null && portObject.getNumber() > 0) {
748 Long portNumber = portObject.getNumber();
749
750 if (sw.getId() == inSwitch && portNumber.shortValue() == inPort) {
751 // This is the port that the ARP message came in,
752 // so don't broadcast out this port
753 continue;
754 }
755 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
Jonathan Hart23701d12014-04-03 10:45:48 -0700756 new net.onrc.onos.core.util.Port(portNumber.shortValue())));
TeruU7feef8a2014-04-03 00:15:49 -0700757 actions.add(new OFActionOutput(portNumber.shortValue()));
758 }
759 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700760
761 po.setActions(actions);
762 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
763 po.setActionsLength(actionsLength);
764 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
765 + arpRequest.length);
766
767 flowPusher.add(sw, po);
768 }
769
770 if (log.isTraceEnabled()) {
771 log.trace("Broadcast ARP request to: {}", switchPorts);
772 }
773 }
774
775 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
776 if (log.isTraceEnabled()) {
777 log.trace("Sending ARP request out {}/{}",
778 HexString.toHexString(dpid), port);
779 }
780
781 OFPacketOut po = new OFPacketOut();
782 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
783 .setPacketData(arpRequest);
784
785 List<OFAction> actions = new ArrayList<OFAction>();
786 actions.add(new OFActionOutput(port));
787 po.setActions(actions);
788 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
789 po.setActionsLength(actionsLength);
790 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
791 + arpRequest.length);
792
793 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
794
795 if (sw == null) {
796 log.warn("Switch not found when sending ARP request");
797 return;
798 }
799
800 flowPusher.add(sw, po);
801 }
802
803 private void sendArpReply(ARP arpRequest, long dpid, short port,
804 MACAddress targetMac) {
805 if (log.isTraceEnabled()) {
806 log.trace(
807 "Sending reply {} => {} to {}",
808 new Object[] {
809 inetAddressToString(arpRequest
810 .getTargetProtocolAddress()),
811 targetMac,
812 inetAddressToString(arpRequest
813 .getSenderProtocolAddress())});
814 }
815
816 ARP arpReply = new ARP();
817 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
818 .setProtocolType(ARP.PROTO_TYPE_IP)
819 .setHardwareAddressLength(
820 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
821 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
822 .setOpCode(ARP.OP_REPLY)
823 .setSenderHardwareAddress(targetMac.toBytes())
824 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
825 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
826 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
827
828 Ethernet eth = new Ethernet();
829 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
830 .setSourceMACAddress(targetMac.toBytes())
831 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
832
833 if (vlan != NO_VLAN) {
834 eth.setVlanID(vlan).setPriorityCode((byte) 0);
835 }
836
837 List<OFAction> actions = new ArrayList<OFAction>();
838 actions.add(new OFActionOutput(port));
839
840 OFPacketOut po = new OFPacketOut();
841 po.setInPort(OFPort.OFPP_NONE)
842 .setBufferId(-1)
843 .setPacketData(eth.serialize())
844 .setActions(actions)
845 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
846 .setLengthU(
847 OFPacketOut.MINIMUM_LENGTH
848 + OFActionOutput.MINIMUM_LENGTH
849 + po.getPacketData().length);
850
851 List<OFMessage> msgList = new ArrayList<OFMessage>();
852 msgList.add(po);
853
854 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
855
856 if (sw == null) {
857 log.warn("Switch {} not found when sending ARP reply",
858 HexString.toHexString(dpid));
859 return;
860 }
861
862 flowPusher.add(sw, po);
863 }
864
865 private String inetAddressToString(byte[] bytes) {
866 try {
867 return InetAddress.getByAddress(bytes).getHostAddress();
868 } catch (UnknownHostException e) {
869 log.debug("Invalid IP address", e);
870 return "";
871 }
872 }
873
874 /*
875 * IProxyArpService methods
876 */
877
878 @Override
879 public MACAddress getMacAddress(InetAddress ipAddress) {
880 // return arpCache.lookup(ipAddress);
881 return null;
882 }
883
884 @Override
885 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
886 boolean retry) {
887 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
888
889 // Sanity check to make sure we don't send a request for our own address
890 if (!configService.isInterfaceAddress(ipAddress)) {
891 sendArpRequestForAddress(ipAddress);
892 }
893 }
894
895 @Override
896 public List<String> getMappings() {
897 return new ArrayList<String>();
898 }
899
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700900 private void sendArpReplyToWaitingRequesters(InetAddress address,
901 MACAddress mac) {
902 log.debug("Sending ARP reply for {} to requesters",
903 address.getHostAddress());
904
905 // See if anyone's waiting for this ARP reply
906 Set<ArpRequest> requests = arpRequests.get(address);
907
908 // Synchronize on the Multimap while using an iterator for one of the
909 // sets
910 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
911 requests.size());
912 synchronized (arpRequests) {
913 Iterator<ArpRequest> it = requests.iterator();
914 while (it.hasNext()) {
915 ArpRequest request = it.next();
916 it.remove();
917 requestsToSend.add(request);
918 }
919 }
920
TeruU7feef8a2014-04-03 00:15:49 -0700921 //TODO here, comment outed from long time ago. I will check if we need it later.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700922 /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
923 InetAddresses.coerceToInteger(address));
924
925 MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
926
927 log.debug("Found {} at {} in network map",
928 address.getHostAddress(), mac);*/
929
930 // Don't hold an ARP lock while dispatching requests
931 for (ArpRequest request : requestsToSend) {
932 request.dispatchReply(address, mac);
933 }
934 }
TeruU7feef8a2014-04-03 00:15:49 -0700935}