blob: 46911495968538ad5b17c35d23d2293bac01e8df [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,
Ray Milkey269ffb92014-04-03 14:43:30 -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();
Ray Milkey269ffb92014-04-03 14:43:30 -070075 private BroadcastPacketOutEventHandler broadcastPacketOutEventHandler = new BroadcastPacketOutEventHandler();
TeruU7feef8a2014-04-03 00:15:49 -070076 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;
Ray Milkey269ffb92014-04-03 14:43:30 -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
Ray Milkey269ffb92014-04-03 14:43:30 -070092 IEventChannelListener<Long, BroadcastPacketOutNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070093
Ray Milkey269ffb92014-04-03 14:43:30 -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 }
119 }
120
121 @Override
122 public void entryUpdated(BroadcastPacketOutNotification value) {
123 log.debug("entryUpdated");
124 // TODO: For now, entryUpdated() is processed as entryAdded()
125 entryAdded(value);
126 }
127
128 @Override
129 public void entryRemoved(BroadcastPacketOutNotification value) {
130 log.debug("entryRemoved");
131 // TODO: Not implemented. Revisit when this module is refactored
132 }
TeruU7feef8a2014-04-03 00:15:49 -0700133 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700134
TeruU7feef8a2014-04-03 00:15:49 -0700135 private class SinglePacketOutEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -0700136 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
Ray Milkey269ffb92014-04-03 14:43:30 -0700178 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700179
Ray Milkey269ffb92014-04-03 14:43:30 -0700180 @Override
181 public void entryAdded(ArpReplyNotification arpReply) {
182 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 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700192
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 if (addr != null) {
194 sendArpReplyToWaitingRequesters(addr,
195 arpReply.getTargetMacAddress());
196 }
197 }
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 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700209 }
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,
Ray Milkey269ffb92014-04-03 14:43:30 -0700237 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700238 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 }
Ray Milkey269ffb92014-04-03 14:43:30 -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) {
Ray Milkey269ffb92014-04-03 14:43:30 -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);
Ray Milkey269ffb92014-04-03 14:43:30 -0700323 networkGraph = networkGraphService.getNetworkGraph();
324
325 //
326 // Event notification setup: channels and event handlers
327 //
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
338 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
339 arpReplyEventHandler,
340 Long.class,
341 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
Ray Milkey269ffb92014-04-03 14:43:30 -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,
Ray Milkey269ffb92014-04-03 14:43:30 -0700463 Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700464 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
Ray Milkey269ffb92014-04-03 14:43:30 -0700495 arpRequests.put(target, new ArpRequest(
496 new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800497
Ray Milkey269ffb92014-04-03 14:43:30 -0700498 Device targetDevice = networkGraph.getDeviceByMac(MACAddress.valueOf(arp.getTargetHardwareAddress()));
pingping-linb8757bf2013-12-13 01:48:58 +0800499
Ray Milkey269ffb92014-04-03 14:43:30 -0700500 if (targetDevice == null) {
501 if (log.isTraceEnabled()) {
502 log.trace("No device info found for {} - broadcasting",
503 target.getHostAddress());
504 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800505
Ray Milkey269ffb92014-04-03 14:43:30 -0700506 // 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 } else {
514 // Even if the device exists in our database, we do not reply to
515 // the request directly, but check whether the device is still valid
516 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200517
Ray Milkey269ffb92014-04-03 14:43:30 -0700518 if (log.isTraceEnabled()) {
519 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
520 new Object[]{
521 inetAddressToString(arp.getTargetProtocolAddress()),
522 macAddress,
523 HexString.toHexString(sw.getId()), pi.getInPort()});
524 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200525
Ray Milkey269ffb92014-04-03 14:43:30 -0700526 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800527
Ray Milkey269ffb92014-04-03 14:43:30 -0700528 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
529
530 if (!outPorts.iterator().hasNext()) {
531 if (log.isTraceEnabled()) {
532 log.trace("Device {} exists but is not connected to any ports" +
533 " - broadcasting", macAddress);
534 }
535
536// BroadcastPacketOutNotification key =
Ray Milkey5d406012014-04-08 14:44:41 -0700537// new BroadcastPacketOutNotification(eth.serialize(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700538// target, sw.getId(), pi.getInPort());
539// broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
540 } else {
541 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
542 //long outSwitch = 0;
543 //short outPort = 0;
544
545 if (portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
546 continue;
547 }
548
549 short outPort = portObject.getNumber().shortValue();
550 Switch outSwitchObject = portObject.getSwitch();
551 long outSwitch = outSwitchObject.getDpid();
552
553 if (log.isTraceEnabled()) {
554 log.trace("Probing device {} on port {}/{}",
555 new Object[]{macAddress,
556 HexString.toHexString(outSwitch), outPort});
557 }
558
559 SinglePacketOutNotification key =
560 new SinglePacketOutNotification(eth.serialize(),
561 ByteBuffer.wrap(target.getAddress()).getInt(), outSwitch, outPort);
562 singlePacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
563 }
564 }
565 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700566 }
pingping-linb8757bf2013-12-13 01:48:58 +0800567
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700568 // Not used because device manager currently updates the database
569 // for ARP replies. May be useful in the future.
570 private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp) {
571 if (log.isTraceEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700572 log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[]{
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700573 inetAddressToString(arp.getSenderProtocolAddress()),
574 HexString.toHexString(arp.getSenderHardwareAddress()),
575 HexString.toHexString(sw.getId()), pi.getInPort()});
576 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800577
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700578 InetAddress senderIpAddress;
579 try {
580 senderIpAddress = InetAddress.getByAddress(arp
581 .getSenderProtocolAddress());
582 } catch (UnknownHostException e) {
583 log.debug("Invalid address in ARP reply", e);
584 return;
585 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800586
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700587 MACAddress senderMacAddress = MACAddress.valueOf(arp
588 .getSenderHardwareAddress());
589
590 // See if anyone's waiting for this ARP reply
591 Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
592
593 // Synchronize on the Multimap while using an iterator for one of the
594 // sets
595 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
596 requests.size());
597 synchronized (arpRequests) {
598 Iterator<ArpRequest> it = requests.iterator();
599 while (it.hasNext()) {
600 ArpRequest request = it.next();
601 it.remove();
602 requestsToSend.add(request);
603 }
604 }
605
606 // Don't hold an ARP lock while dispatching requests
607 for (ArpRequest request : requestsToSend) {
608 request.dispatchReply(senderIpAddress, senderMacAddress);
609 }
610 }
611
612 private void sendArpRequestForAddress(InetAddress ipAddress) {
613 // TODO what should the sender IP address and MAC address be if no
614 // IP addresses are configured? Will there ever be a need to send
615 // ARP requests from the controller in that case?
616 // All-zero MAC address doesn't seem to work - hosts don't respond to it
617
618 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
619 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
620 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
621 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
622 (byte) 0xff, (byte) 0xff, (byte) 0xff};
623
624 ARP arpRequest = new ARP();
625
626 arpRequest
627 .setHardwareType(ARP.HW_TYPE_ETHERNET)
628 .setProtocolType(ARP.PROTO_TYPE_IP)
629 .setHardwareAddressLength(
630 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
631 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
632 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
633 .setTargetProtocolAddress(ipAddress.getAddress());
634
635 MACAddress routerMacAddress = configService.getRouterMacAddress();
636 // TODO hack for now as it's unclear what the MAC address should be
637 byte[] senderMacAddress = genericNonZeroMac;
638 if (routerMacAddress != null) {
639 senderMacAddress = routerMacAddress.toBytes();
640 }
641 arpRequest.setSenderHardwareAddress(senderMacAddress);
642
643 byte[] senderIPAddress = zeroIpv4;
644 Interface intf = configService.getOutgoingInterface(ipAddress);
645 if (intf != null) {
646 senderIPAddress = intf.getIpAddress().getAddress();
647 }
648
649 arpRequest.setSenderProtocolAddress(senderIPAddress);
650
651 Ethernet eth = new Ethernet();
652 eth.setSourceMACAddress(senderMacAddress)
653 .setDestinationMACAddress(broadcastMac)
654 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
655
656 if (vlan != NO_VLAN) {
657 eth.setVlanID(vlan).setPriorityCode((byte) 0);
658 }
659
660 // sendArpRequestToSwitches(ipAddress, eth.serialize());
Ray Milkey269ffb92014-04-03 14:43:30 -0700661 SinglePacketOutNotification key =
662 new SinglePacketOutNotification(eth.serialize(), ByteBuffer.wrap(ipAddress.getAddress()).getInt(),
663 intf.getDpid(), intf.getPort());
664 singlePacketOutEventChannel.addTransientEntry(MACAddress.valueOf(senderMacAddress).toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700665 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700666
TeruU7feef8a2014-04-03 00:15:49 -0700667 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700668 sendArpRequestToSwitches(dstAddress, arpRequest, 0,
669 OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700670 }
671
672 private void sendArpRequestToSwitches(InetAddress dstAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700673 byte[] arpRequest, long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700674
675 if (configService.hasLayer3Configuration()) {
676 Interface intf = configService.getOutgoingInterface(dstAddress);
677 if (intf == null) {
678 // TODO here it should be broadcast out all non-interface edge
679 // ports.
680 // I think we can assume that if it's not a request for an
681 // external
682 // network, it's an ARP for a host in our own network. So we
683 // want to
684 // send it out all edge ports that don't have an interface
685 // configured
686 // to ensure it reaches all hosts in our network.
687 log.debug("No interface found to send ARP request for {}",
688 dstAddress.getHostAddress());
689 } else {
690 sendArpRequestOutPort(arpRequest, intf.getDpid(),
691 intf.getPort());
692 }
693 } else {
694 // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
695 broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
696 }
697 }
698
699 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
700 ARP arp = (ARP) eth.getPayload();
701
702 if (log.isTraceEnabled()) {
703 log.trace("Sending ARP reply for {} to other ONOS instances",
704 inetAddressToString(arp.getSenderProtocolAddress()));
705 }
706
707 InetAddress targetAddress;
708
709 try {
710 targetAddress = InetAddress.getByAddress(arp
711 .getSenderProtocolAddress());
712 } catch (UnknownHostException e) {
713 log.error("Unknown host", e);
714 return;
715 }
716
717 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
718
Ray Milkey269ffb92014-04-03 14:43:30 -0700719 ArpReplyNotification key =
720 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
721 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
722 arpReplyEventChannel.addTransientEntry(mac.toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700723 }
724
725 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
Ray Milkey269ffb92014-04-03 14:43:30 -0700726 short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700727 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
728
729 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
730
731 OFPacketOut po = new OFPacketOut();
732 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
733 .setPacketData(arpRequest);
734
735 List<OFAction> actions = new ArrayList<OFAction>();
736
Ray Milkey269ffb92014-04-03 14:43:30 -0700737 Switch graphSw = networkGraph.getSwitch(sw.getId());
738 Collection<net.onrc.onos.core.topology.Port> ports = graphSw.getPorts();
739
740 if (ports == null) {
741 continue;
742 }
743
744 for (net.onrc.onos.core.topology.Port portObject : ports) {
745 if (portObject.getOutgoingLink() == null && portObject.getNumber() > 0) {
746 Long portNumber = portObject.getNumber();
747
748 if (sw.getId() == inSwitch && portNumber.shortValue() == inPort) {
749 // This is the port that the ARP message came in,
750 // so don't broadcast out this port
751 continue;
752 }
753 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
754 new net.onrc.onos.core.util.Port(portNumber.shortValue())));
755 actions.add(new OFActionOutput(portNumber.shortValue()));
756 }
757 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700758
759 po.setActions(actions);
760 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
761 po.setActionsLength(actionsLength);
762 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
763 + arpRequest.length);
764
765 flowPusher.add(sw, po);
766 }
767
768 if (log.isTraceEnabled()) {
769 log.trace("Broadcast ARP request to: {}", switchPorts);
770 }
771 }
772
773 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
774 if (log.isTraceEnabled()) {
775 log.trace("Sending ARP request out {}/{}",
776 HexString.toHexString(dpid), port);
777 }
778
779 OFPacketOut po = new OFPacketOut();
780 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
781 .setPacketData(arpRequest);
782
783 List<OFAction> actions = new ArrayList<OFAction>();
784 actions.add(new OFActionOutput(port));
785 po.setActions(actions);
786 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
787 po.setActionsLength(actionsLength);
788 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
789 + arpRequest.length);
790
791 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
792
793 if (sw == null) {
794 log.warn("Switch not found when sending ARP request");
795 return;
796 }
797
798 flowPusher.add(sw, po);
799 }
800
801 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700802 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700803 if (log.isTraceEnabled()) {
804 log.trace(
805 "Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700806 new Object[]{
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700807 inetAddressToString(arpRequest
808 .getTargetProtocolAddress()),
809 targetMac,
810 inetAddressToString(arpRequest
811 .getSenderProtocolAddress())});
812 }
813
814 ARP arpReply = new ARP();
815 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
816 .setProtocolType(ARP.PROTO_TYPE_IP)
817 .setHardwareAddressLength(
818 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
819 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
820 .setOpCode(ARP.OP_REPLY)
821 .setSenderHardwareAddress(targetMac.toBytes())
822 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
823 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
824 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
825
826 Ethernet eth = new Ethernet();
827 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
828 .setSourceMACAddress(targetMac.toBytes())
829 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
830
831 if (vlan != NO_VLAN) {
832 eth.setVlanID(vlan).setPriorityCode((byte) 0);
833 }
834
835 List<OFAction> actions = new ArrayList<OFAction>();
836 actions.add(new OFActionOutput(port));
837
838 OFPacketOut po = new OFPacketOut();
839 po.setInPort(OFPort.OFPP_NONE)
840 .setBufferId(-1)
841 .setPacketData(eth.serialize())
842 .setActions(actions)
843 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
844 .setLengthU(
845 OFPacketOut.MINIMUM_LENGTH
846 + OFActionOutput.MINIMUM_LENGTH
847 + po.getPacketData().length);
848
849 List<OFMessage> msgList = new ArrayList<OFMessage>();
850 msgList.add(po);
851
852 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
853
854 if (sw == null) {
855 log.warn("Switch {} not found when sending ARP reply",
856 HexString.toHexString(dpid));
857 return;
858 }
859
860 flowPusher.add(sw, po);
861 }
862
863 private String inetAddressToString(byte[] bytes) {
864 try {
865 return InetAddress.getByAddress(bytes).getHostAddress();
866 } catch (UnknownHostException e) {
867 log.debug("Invalid IP address", e);
868 return "";
869 }
870 }
871
872 /*
873 * IProxyArpService methods
874 */
875
876 @Override
877 public MACAddress getMacAddress(InetAddress ipAddress) {
878 // return arpCache.lookup(ipAddress);
879 return null;
880 }
881
882 @Override
883 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700884 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700885 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
886
887 // Sanity check to make sure we don't send a request for our own address
888 if (!configService.isInterfaceAddress(ipAddress)) {
889 sendArpRequestForAddress(ipAddress);
890 }
891 }
892
893 @Override
894 public List<String> getMappings() {
895 return new ArrayList<String>();
896 }
897
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700898 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700899 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700900 log.debug("Sending ARP reply for {} to requesters",
901 address.getHostAddress());
902
903 // See if anyone's waiting for this ARP reply
904 Set<ArpRequest> requests = arpRequests.get(address);
905
906 // Synchronize on the Multimap while using an iterator for one of the
907 // sets
908 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
909 requests.size());
910 synchronized (arpRequests) {
911 Iterator<ArpRequest> it = requests.iterator();
912 while (it.hasNext()) {
913 ArpRequest request = it.next();
914 it.remove();
915 requestsToSend.add(request);
916 }
917 }
918
TeruU7feef8a2014-04-03 00:15:49 -0700919 //TODO here, comment outed from long time ago. I will check if we need it later.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700920 /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
Ray Milkey269ffb92014-04-03 14:43:30 -0700921 InetAddresses.coerceToInteger(address));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700922
923 MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
924
925 log.debug("Found {} at {} in network map",
Ray Milkey269ffb92014-04-03 14:43:30 -0700926 address.getHostAddress(), mac);*/
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700927
928 // Don't hold an ARP lock while dispatching requests
929 for (ArpRequest request : requestsToSend) {
930 request.dispatchReply(address, mac);
931 }
932 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700933}