blob: 017b260a50f71df6e20003f839ef2f66fa7959a2 [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) {
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700437 if (!(msg instanceof OFPacketIn)) {
438 return Command.CONTINUE;
439 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700440
441 OFPacketIn pi = (OFPacketIn) msg;
442
443 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200444 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200445
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700446 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
447 ARP arp = (ARP) eth.getPayload();
448 if (arp.getOpCode() == ARP.OP_REQUEST) {
449 handleArpRequest(sw, pi, arp, eth);
450 } else if (arp.getOpCode() == ARP.OP_REPLY) {
451 // For replies we simply send a notification via Hazelcast
452 sendArpReplyNotification(eth, pi);
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200453
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700454 // handleArpReply(sw, pi, arp);
455 }
pingping-linb8757bf2013-12-13 01:48:58 +0800456
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700457 // Stop ARP packets here
458 return Command.STOP;
459 }
pingping-linb8757bf2013-12-13 01:48:58 +0800460
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700461 // Propagate everything else
462 return Command.CONTINUE;
463 }
pingping-linb8757bf2013-12-13 01:48:58 +0800464
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700465 private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
Ray Milkey269ffb92014-04-03 14:43:30 -0700466 Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700467 if (log.isTraceEnabled()) {
468 log.trace("ARP request received for {}",
469 inetAddressToString(arp.getTargetProtocolAddress()));
470 }
pingping-linb8757bf2013-12-13 01:48:58 +0800471
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700472 InetAddress target;
473 try {
474 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
475 } catch (UnknownHostException e) {
476 log.debug("Invalid address in ARP request", e);
477 return;
478 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700479
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700480 if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
481 // If the request came from outside our network, we only care if
482 // it was a request for one of our interfaces.
483 if (configService.isInterfaceAddress(target)) {
484 log.trace(
485 "ARP request for our interface. Sending reply {} => {}",
486 target.getHostAddress(),
487 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800488
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700489 sendArpReply(arp, sw.getId(), pi.getInPort(),
490 configService.getRouterMacAddress());
491 }
pingping-linb8757bf2013-12-13 01:48:58 +0800492
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700493 return;
494 }
pingping-linb8757bf2013-12-13 01:48:58 +0800495
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700496 // MACAddress macAddress = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800497
Ray Milkey269ffb92014-04-03 14:43:30 -0700498 arpRequests.put(target, new ArpRequest(
499 new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800500
Ray Milkey269ffb92014-04-03 14:43:30 -0700501 Device targetDevice = networkGraph.getDeviceByMac(MACAddress.valueOf(arp.getTargetHardwareAddress()));
pingping-linb8757bf2013-12-13 01:48:58 +0800502
Ray Milkey269ffb92014-04-03 14:43:30 -0700503 if (targetDevice == null) {
504 if (log.isTraceEnabled()) {
505 log.trace("No device info found for {} - broadcasting",
506 target.getHostAddress());
507 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800508
Ray Milkey269ffb92014-04-03 14:43:30 -0700509 // We don't know the device so broadcast the request out
510 BroadcastPacketOutNotification key =
511 new BroadcastPacketOutNotification(eth.serialize(),
512 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort());
513 log.debug("broadcastPacketOutEventChannel mac {}, ip {}, dpid {}, port {}, paket {}", eth.getSourceMAC().toLong(),
514 ByteBuffer.wrap(arp.getTargetProtocolAddress()).getInt(), sw.getId(), pi.getInPort(), eth.serialize().length);
515 broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
516 } else {
517 // Even if the device exists in our database, we do not reply to
518 // the request directly, but check whether the device is still valid
519 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200520
Ray Milkey269ffb92014-04-03 14:43:30 -0700521 if (log.isTraceEnabled()) {
522 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
523 new Object[]{
524 inetAddressToString(arp.getTargetProtocolAddress()),
525 macAddress,
526 HexString.toHexString(sw.getId()), pi.getInPort()});
527 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200528
Ray Milkey269ffb92014-04-03 14:43:30 -0700529 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800530
Ray Milkey269ffb92014-04-03 14:43:30 -0700531 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
532
533 if (!outPorts.iterator().hasNext()) {
534 if (log.isTraceEnabled()) {
535 log.trace("Device {} exists but is not connected to any ports" +
536 " - broadcasting", macAddress);
537 }
538
539// BroadcastPacketOutNotification key =
Ray Milkey5d406012014-04-08 14:44:41 -0700540// new BroadcastPacketOutNotification(eth.serialize(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700541// target, sw.getId(), pi.getInPort());
542// broadcastPacketOutEventChannel.addTransientEntry(eth.getDestinationMAC().toLong(), key);
543 } else {
544 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
545 //long outSwitch = 0;
546 //short outPort = 0;
547
548 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()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700575 log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[]{
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700576 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);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700648 if (intf == null) {
649 // TODO handle the case where the controller needs to send an ARP
650 // request but there's not IP configuration. In this case the
651 // request should be broadcast out all edge ports in the network.
652 log.warn("Sending ARP requests with default configuration "
653 + "not supported");
654 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700655 }
656
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700657 senderIPAddress = intf.getIpAddress().getAddress();
658
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700659 arpRequest.setSenderProtocolAddress(senderIPAddress);
660
661 Ethernet eth = new Ethernet();
662 eth.setSourceMACAddress(senderMacAddress)
663 .setDestinationMACAddress(broadcastMac)
664 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
665
666 if (vlan != NO_VLAN) {
667 eth.setVlanID(vlan).setPriorityCode((byte) 0);
668 }
669
670 // sendArpRequestToSwitches(ipAddress, eth.serialize());
Ray Milkey269ffb92014-04-03 14:43:30 -0700671 SinglePacketOutNotification key =
672 new SinglePacketOutNotification(eth.serialize(), ByteBuffer.wrap(ipAddress.getAddress()).getInt(),
673 intf.getDpid(), intf.getPort());
674 singlePacketOutEventChannel.addTransientEntry(MACAddress.valueOf(senderMacAddress).toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700675 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700676
TeruU7feef8a2014-04-03 00:15:49 -0700677 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700678 sendArpRequestToSwitches(dstAddress, arpRequest, 0,
679 OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700680 }
681
682 private void sendArpRequestToSwitches(InetAddress dstAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700683 byte[] arpRequest, long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700684
685 if (configService.hasLayer3Configuration()) {
686 Interface intf = configService.getOutgoingInterface(dstAddress);
687 if (intf == null) {
688 // TODO here it should be broadcast out all non-interface edge
689 // ports.
690 // I think we can assume that if it's not a request for an
691 // external
692 // network, it's an ARP for a host in our own network. So we
693 // want to
694 // send it out all edge ports that don't have an interface
695 // configured
696 // to ensure it reaches all hosts in our network.
697 log.debug("No interface found to send ARP request for {}",
698 dstAddress.getHostAddress());
699 } else {
700 sendArpRequestOutPort(arpRequest, intf.getDpid(),
701 intf.getPort());
702 }
703 } else {
704 // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
705 broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
706 }
707 }
708
709 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
710 ARP arp = (ARP) eth.getPayload();
711
712 if (log.isTraceEnabled()) {
713 log.trace("Sending ARP reply for {} to other ONOS instances",
714 inetAddressToString(arp.getSenderProtocolAddress()));
715 }
716
717 InetAddress targetAddress;
718
719 try {
720 targetAddress = InetAddress.getByAddress(arp
721 .getSenderProtocolAddress());
722 } catch (UnknownHostException e) {
723 log.error("Unknown host", e);
724 return;
725 }
726
727 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
728
Ray Milkey269ffb92014-04-03 14:43:30 -0700729 ArpReplyNotification key =
730 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
731 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
732 arpReplyEventChannel.addTransientEntry(mac.toLong(), key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700733 }
734
735 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
Ray Milkey269ffb92014-04-03 14:43:30 -0700736 short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700737 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
738
739 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
740
741 OFPacketOut po = new OFPacketOut();
742 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
743 .setPacketData(arpRequest);
744
745 List<OFAction> actions = new ArrayList<OFAction>();
746
Ray Milkey269ffb92014-04-03 14:43:30 -0700747 Switch graphSw = networkGraph.getSwitch(sw.getId());
748 Collection<net.onrc.onos.core.topology.Port> ports = graphSw.getPorts();
749
750 if (ports == null) {
751 continue;
752 }
753
754 for (net.onrc.onos.core.topology.Port portObject : ports) {
755 if (portObject.getOutgoingLink() == null && portObject.getNumber() > 0) {
756 Long portNumber = portObject.getNumber();
757
758 if (sw.getId() == inSwitch && portNumber.shortValue() == inPort) {
759 // This is the port that the ARP message came in,
760 // so don't broadcast out this port
761 continue;
762 }
763 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
764 new net.onrc.onos.core.util.Port(portNumber.shortValue())));
765 actions.add(new OFActionOutput(portNumber.shortValue()));
766 }
767 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700768
769 po.setActions(actions);
770 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
771 po.setActionsLength(actionsLength);
772 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
773 + arpRequest.length);
774
775 flowPusher.add(sw, po);
776 }
777
778 if (log.isTraceEnabled()) {
779 log.trace("Broadcast ARP request to: {}", switchPorts);
780 }
781 }
782
783 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
784 if (log.isTraceEnabled()) {
785 log.trace("Sending ARP request out {}/{}",
786 HexString.toHexString(dpid), port);
787 }
788
789 OFPacketOut po = new OFPacketOut();
790 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
791 .setPacketData(arpRequest);
792
793 List<OFAction> actions = new ArrayList<OFAction>();
794 actions.add(new OFActionOutput(port));
795 po.setActions(actions);
796 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
797 po.setActionsLength(actionsLength);
798 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
799 + arpRequest.length);
800
801 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
802
803 if (sw == null) {
804 log.warn("Switch not found when sending ARP request");
805 return;
806 }
807
808 flowPusher.add(sw, po);
809 }
810
811 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700812 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700813 if (log.isTraceEnabled()) {
814 log.trace(
815 "Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700816 new Object[]{
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700817 inetAddressToString(arpRequest
818 .getTargetProtocolAddress()),
819 targetMac,
820 inetAddressToString(arpRequest
821 .getSenderProtocolAddress())});
822 }
823
824 ARP arpReply = new ARP();
825 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
826 .setProtocolType(ARP.PROTO_TYPE_IP)
827 .setHardwareAddressLength(
828 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
829 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
830 .setOpCode(ARP.OP_REPLY)
831 .setSenderHardwareAddress(targetMac.toBytes())
832 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
833 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
834 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
835
836 Ethernet eth = new Ethernet();
837 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
838 .setSourceMACAddress(targetMac.toBytes())
839 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
840
841 if (vlan != NO_VLAN) {
842 eth.setVlanID(vlan).setPriorityCode((byte) 0);
843 }
844
845 List<OFAction> actions = new ArrayList<OFAction>();
846 actions.add(new OFActionOutput(port));
847
848 OFPacketOut po = new OFPacketOut();
849 po.setInPort(OFPort.OFPP_NONE)
850 .setBufferId(-1)
851 .setPacketData(eth.serialize())
852 .setActions(actions)
853 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
854 .setLengthU(
855 OFPacketOut.MINIMUM_LENGTH
856 + OFActionOutput.MINIMUM_LENGTH
857 + po.getPacketData().length);
858
859 List<OFMessage> msgList = new ArrayList<OFMessage>();
860 msgList.add(po);
861
862 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
863
864 if (sw == null) {
865 log.warn("Switch {} not found when sending ARP reply",
866 HexString.toHexString(dpid));
867 return;
868 }
869
870 flowPusher.add(sw, po);
871 }
872
873 private String inetAddressToString(byte[] bytes) {
874 try {
875 return InetAddress.getByAddress(bytes).getHostAddress();
876 } catch (UnknownHostException e) {
877 log.debug("Invalid IP address", e);
878 return "";
879 }
880 }
881
882 /*
883 * IProxyArpService methods
884 */
885
886 @Override
887 public MACAddress getMacAddress(InetAddress ipAddress) {
888 // return arpCache.lookup(ipAddress);
889 return null;
890 }
891
892 @Override
893 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700894 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700895 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
896
897 // Sanity check to make sure we don't send a request for our own address
898 if (!configService.isInterfaceAddress(ipAddress)) {
899 sendArpRequestForAddress(ipAddress);
900 }
901 }
902
903 @Override
904 public List<String> getMappings() {
905 return new ArrayList<String>();
906 }
907
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700908 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700909 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700910 log.debug("Sending ARP reply for {} to requesters",
911 address.getHostAddress());
912
913 // See if anyone's waiting for this ARP reply
914 Set<ArpRequest> requests = arpRequests.get(address);
915
916 // Synchronize on the Multimap while using an iterator for one of the
917 // sets
918 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
919 requests.size());
920 synchronized (arpRequests) {
921 Iterator<ArpRequest> it = requests.iterator();
922 while (it.hasNext()) {
923 ArpRequest request = it.next();
924 it.remove();
925 requestsToSend.add(request);
926 }
927 }
928
TeruU7feef8a2014-04-03 00:15:49 -0700929 //TODO here, comment outed from long time ago. I will check if we need it later.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700930 /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
Ray Milkey269ffb92014-04-03 14:43:30 -0700931 InetAddresses.coerceToInteger(address));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700932
933 MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
934
935 log.debug("Found {} at {} in network map",
Ray Milkey269ffb92014-04-03 14:43:30 -0700936 address.getHostAddress(), mac);*/
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700937
938 // Don't hold an ARP lock while dispatching requests
939 for (ArpRequest request : requestsToSend) {
940 request.dispatchReply(address, mac);
941 }
942 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700943}