blob: d235c3743c6612f6fd9d8e44b5f2d40a004412b0 [file] [log] [blame]
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12001package net.onrc.onos.ofcontroller.proxyarp;
2
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12003import java.net.InetAddress;
4import java.net.UnknownHostException;
5import java.util.ArrayList;
6import java.util.Collection;
Jonathan Harte93aed42013-12-05 18:39:50 -08007import java.util.HashMap;
Jonathan Hart6261dcd2013-07-22 17:58:35 +12008import java.util.Iterator;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12009import java.util.List;
10import java.util.Map;
11import java.util.Set;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120012import java.util.Timer;
13import java.util.TimerTask;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120014
15import net.floodlightcontroller.core.FloodlightContext;
16import net.floodlightcontroller.core.IFloodlightProviderService;
17import net.floodlightcontroller.core.IOFMessageListener;
18import net.floodlightcontroller.core.IOFSwitch;
Jonathan Harte93aed42013-12-05 18:39:50 -080019import net.floodlightcontroller.core.module.FloodlightModuleContext;
20import net.floodlightcontroller.core.module.IFloodlightModule;
21import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart5afde492013-10-01 12:30:53 +130022import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120023import net.floodlightcontroller.util.MACAddress;
Jonathan Hart18ad55c2013-11-11 22:49:55 -080024import net.onrc.onos.datagrid.IDatagridService;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070025import net.onrc.onos.datagrid.IEventChannel;
26import net.onrc.onos.datagrid.IEventChannelListener;
Jonathan Hart2f790d22013-08-15 14:01:24 +120027import net.onrc.onos.ofcontroller.bgproute.Interface;
Jonathan Hartebba1e12013-10-29 11:37:02 -070028import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
Jonathan Hart7804bea2014-01-07 10:50:52 -080029import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
Jonathan Hartba9ced92013-11-24 16:52:13 -080030import net.onrc.onos.ofcontroller.util.SwitchPort;
Jonathan Hart96892d12014-03-26 20:21:29 -070031import net.onrc.onos.packet.ARP;
32import net.onrc.onos.packet.Ethernet;
33import net.onrc.onos.packet.IPv4;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120034
35import org.openflow.protocol.OFMessage;
36import org.openflow.protocol.OFPacketIn;
37import org.openflow.protocol.OFPacketOut;
38import org.openflow.protocol.OFPort;
39import org.openflow.protocol.OFType;
40import org.openflow.protocol.action.OFAction;
41import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120042import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120043import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
Jonathan Hart4dfc3652013-08-02 20:22:36 +120046import com.google.common.collect.HashMultimap;
47import com.google.common.collect.Multimaps;
48import com.google.common.collect.SetMultimap;
49
Jonathan Hart18ad55c2013-11-11 22:49:55 -080050public class ProxyArpManager implements IProxyArpService, IOFMessageListener,
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070051 IFloodlightModule {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070052 private static final Logger log = LoggerFactory
53 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080054
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070055 private static final long ARP_TIMER_PERIOD = 100; // ms
Jonathan Hartdf6ec332013-08-04 01:37:14 +120056
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070057 private static final int ARP_REQUEST_TIMEOUT = 2000; // ms
Jonathan Hartda4d0e12013-09-30 21:00:20 +130058
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070059 private IFloodlightProviderService floodlightProvider;
60 private IDatagridService datagrid;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070061 private IEventChannel<PacketOutNotification, PacketOutNotification> packetOutEventChannel;
62 private IEventChannel<ArpReplyNotification, ArpReplyNotification> arpReplyEventChannel;
63 private static final String PACKET_OUT_CHANNEL_NAME = "onos.packet_out";
64 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
65 private PacketOutEventHandler packetOutEventHandler =
66 new PacketOutEventHandler();
67 private ArpReplyEventHandler arpReplyEventHandler =
68 new ArpReplyEventHandler();
69
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070070 private IConfigInfoService configService;
71 private IRestApiService restApi;
72 private IFlowPusherService flowPusher;
Jonathan Harte93aed42013-12-05 18:39:50 -080073
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070074 private short vlan;
75 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080076
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070077 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120078
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070079 //
80 // TODO: Using PacketOutNotification as both the key and the
81 // value is a hack that should be removed when this module is
82 // refactored.
83 //
84 private class PacketOutEventHandler implements
85 IEventChannelListener<PacketOutNotification, PacketOutNotification> {
86 @Override
87 public void entryAdded(PacketOutNotification packetOutNotification) {
88 if (packetOutNotification instanceof SinglePacketOutNotification) {
89 SinglePacketOutNotification notification =
90 (SinglePacketOutNotification) packetOutNotification;
91 sendArpRequestOutPort(notification.packet,
92 notification.getOutSwitch(),
93 notification.getOutPort());
94
95 // set timestamp
96 InetAddress addr = notification.getTargetAddress();
97 if (addr != null) {
98 for (ArpRequest request : arpRequests.get(addr)) {
99 request.setRequestTime();
100 }
101 }
102 } else if (packetOutNotification instanceof BroadcastPacketOutNotification) {
103 BroadcastPacketOutNotification notification =
104 (BroadcastPacketOutNotification) packetOutNotification;
105 broadcastArpRequestOutMyEdge(notification.packet,
106 notification.getInSwitch(),
107 notification.getInPort());
108
109 // set timestamp
110 InetAddress addr = notification.getTargetAddress();
111 if (addr != null) {
112 for (ArpRequest request : arpRequests.get(addr)) {
113 request.setRequestTime();
114 }
115 }
116 } else {
117 log.warn("Unknown packet out notification received");
118 }
119 }
120
121 @Override
122 public void entryUpdated(PacketOutNotification packetOutNotification) {
123 // TODO: For now, entryUpdated() is processed as entryAdded()
124 entryAdded(packetOutNotification);
125 }
126
127 @Override
128 public void entryRemoved(PacketOutNotification packetOutNotification) {
129 // TODO: Not implemented. Revisit when this module is refactored
130 }
131 }
132
133 //
134 // TODO: Using ArpReplyNotification as both the key and the
135 // value is a hack that should be removed when this module is
136 // refactored.
137 //
138 private class ArpReplyEventHandler implements
139 IEventChannelListener<ArpReplyNotification, ArpReplyNotification> {
140 @Override
141 public void entryAdded(ArpReplyNotification arpReply) {
142 log.debug("Received ARP reply notification for {}",
143 arpReply.getTargetAddress());
144 sendArpReplyToWaitingRequesters(arpReply.getTargetAddress(),
145 arpReply.getTargetMacAddress());
146 }
147
148 @Override
149 public void entryUpdated(ArpReplyNotification arpReply) {
150 // TODO: For now, entryUpdated() is processed as entryAdded()
151 entryAdded(arpReply);
152 }
153
154 @Override
155 public void entryRemoved(ArpReplyNotification arpReply) {
156 // TODO: Not implemented. Revisit when this module is refactored
157 }
158 }
159
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700160 private static class ArpRequest {
161 private final IArpRequester requester;
162 private final boolean retry;
163 private boolean sent = false;
164 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200165
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700166 public ArpRequest(IArpRequester requester, boolean retry) {
167 this.requester = requester;
168 this.retry = retry;
169 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200170
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700171 public ArpRequest(ArpRequest old) {
172 this.requester = old.requester;
173 this.retry = old.retry;
174 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200175
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700176 public boolean isExpired() {
177 return sent
178 && ((System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT);
179 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200180
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700181 public boolean shouldRetry() {
182 return retry;
183 }
184
185 public void dispatchReply(InetAddress ipAddress,
186 MACAddress replyMacAddress) {
187 requester.arpResponse(ipAddress, replyMacAddress);
188 }
189
190 public void setRequestTime() {
191 this.requestTime = System.currentTimeMillis();
192 this.sent = true;
193 }
194 }
195
196 private class HostArpRequester implements IArpRequester {
197 private final ARP arpRequest;
198 private final long dpid;
199 private final short port;
200
201 public HostArpRequester(ARP arpRequest, long dpid, short port) {
202 this.arpRequest = arpRequest;
203 this.dpid = dpid;
204 this.port = port;
205 }
206
207 @Override
208 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
209 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
210 macAddress);
211 }
212 }
213
214 @Override
215 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
216 Collection<Class<? extends IFloodlightService>> l =
217 new ArrayList<Class<? extends IFloodlightService>>();
218 l.add(IProxyArpService.class);
219 return l;
220 }
221
222 @Override
223 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
224 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
225 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
226 m.put(IProxyArpService.class, this);
227 return m;
228 }
229
230 @Override
231 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
232 Collection<Class<? extends IFloodlightService>> dependencies =
233 new ArrayList<Class<? extends IFloodlightService>>();
234 dependencies.add(IFloodlightProviderService.class);
235 dependencies.add(IRestApiService.class);
236 dependencies.add(IDatagridService.class);
237 dependencies.add(IConfigInfoService.class);
238 dependencies.add(IFlowPusherService.class);
239 return dependencies;
240 }
241
242 @Override
243 public void init(FloodlightModuleContext context) {
244 this.floodlightProvider = context
245 .getServiceImpl(IFloodlightProviderService.class);
246 this.datagrid = context.getServiceImpl(IDatagridService.class);
247 this.configService = context.getServiceImpl(IConfigInfoService.class);
248 this.restApi = context.getServiceImpl(IRestApiService.class);
249 this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
250
251 // arpCache = new ArpCache();
252
253 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
254 .<InetAddress, ArpRequest>create());
255
256 }
257
258 @Override
259 public void startUp(FloodlightModuleContext context) {
260 this.vlan = configService.getVlan();
261 log.info("vlan set to {}", this.vlan);
262
263 restApi.addRestletRoutable(new ArpWebRoutable());
264 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
265
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700266 //
267 // Event notification setup: channels and event handlers
268 //
269 //
270 // TODO: Using PacketOutNotification or ArpReplyNotification as both
271 // the key and the value is a hack that should be removed when this
272 // module is refactored.
273 //
274 packetOutEventChannel = datagrid.addListener(PACKET_OUT_CHANNEL_NAME,
275 packetOutEventHandler,
276 PacketOutNotification.class,
277 PacketOutNotification.class);
278 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
279 arpReplyEventHandler,
280 ArpReplyNotification.class,
281 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700282
283 Timer arpTimer = new Timer("arp-processing");
284 arpTimer.scheduleAtFixedRate(new TimerTask() {
285 @Override
286 public void run() {
287 doPeriodicArpProcessing();
288 }
289 }, 0, ARP_TIMER_PERIOD);
290 }
291
292 /*
293 * Function that runs periodically to manage the asynchronous request mechanism.
294 * It basically cleans up old ARP requests if we don't get a response for them.
295 * The caller can designate that a request should be retried indefinitely, and
296 * this task will handle that as well.
297 */
298 private void doPeriodicArpProcessing() {
299 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
300 .<InetAddress, ArpRequest>create();
301
302 // Have to synchronize externally on the Multimap while using an
303 // iterator,
304 // even though it's a synchronizedMultimap
305 synchronized (arpRequests) {
306 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
307 .entries().iterator();
308
309 while (it.hasNext()) {
310 Map.Entry<InetAddress, ArpRequest> entry = it.next();
311 ArpRequest request = entry.getValue();
312 if (request.isExpired()) {
313 log.debug("Cleaning expired ARP request for {}", entry
314 .getKey().getHostAddress());
315
316 // If the ARP request is expired and then delete the device
317 // TODO check whether this is OK from this thread
318 // TODO: Fix the code below after deviceStorage was removed
319 /*
320 IDeviceObject targetDevice =
321 deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(entry.getKey()));
322 if (targetDevice != null) {
323 deviceStorage.removeDevice(targetDevice);
324 if (log.isDebugEnabled()) {
325 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDevice);
326 }
327 }
328 */
329
330 it.remove();
331
332 if (request.shouldRetry()) {
333 retryList.put(entry.getKey(), request);
334 }
335 }
336 }
337 }
338
339 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
340 .asMap().entrySet()) {
341
342 InetAddress address = entry.getKey();
343
344 log.debug("Resending ARP request for {}", address.getHostAddress());
345
346 // Only ARP requests sent by the controller will have the retry flag
347 // set, so for now we can just send a new ARP request for that
348 // address.
349 sendArpRequestForAddress(address);
350
351 for (ArpRequest request : entry.getValue()) {
352 arpRequests.put(address, new ArpRequest(request));
353 }
354 }
355 }
356
357 @Override
358 public String getName() {
359 return "proxyarpmanager";
360 }
361
362 @Override
363 public boolean isCallbackOrderingPrereq(OFType type, String name) {
364 if (type == OFType.PACKET_IN) {
365 return "devicemanager".equals(name)
366 || "onosdevicemanager".equals(name);
367 } else {
368 return false;
369 }
370 }
371
372 @Override
373 public boolean isCallbackOrderingPostreq(OFType type, String name) {
374 return type == OFType.PACKET_IN && "onosforwarding".equals(name);
375 }
376
377 @Override
378 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
379
380 OFPacketIn pi = (OFPacketIn) msg;
381
382 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200383 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200384
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700385 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
386 ARP arp = (ARP) eth.getPayload();
387 if (arp.getOpCode() == ARP.OP_REQUEST) {
388 handleArpRequest(sw, pi, arp, eth);
389 } else if (arp.getOpCode() == ARP.OP_REPLY) {
390 // For replies we simply send a notification via Hazelcast
391 sendArpReplyNotification(eth, pi);
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200392
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700393 // handleArpReply(sw, pi, arp);
394 }
pingping-linb8757bf2013-12-13 01:48:58 +0800395
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700396 // Stop ARP packets here
397 return Command.STOP;
398 }
pingping-linb8757bf2013-12-13 01:48:58 +0800399
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700400 // Propagate everything else
401 return Command.CONTINUE;
402 }
pingping-linb8757bf2013-12-13 01:48:58 +0800403
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700404 private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
405 Ethernet eth) {
406 if (log.isTraceEnabled()) {
407 log.trace("ARP request received for {}",
408 inetAddressToString(arp.getTargetProtocolAddress()));
409 }
pingping-linb8757bf2013-12-13 01:48:58 +0800410
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700411 InetAddress target;
412 try {
413 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
414 } catch (UnknownHostException e) {
415 log.debug("Invalid address in ARP request", e);
416 return;
417 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700418
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700419 if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
420 // If the request came from outside our network, we only care if
421 // it was a request for one of our interfaces.
422 if (configService.isInterfaceAddress(target)) {
423 log.trace(
424 "ARP request for our interface. Sending reply {} => {}",
425 target.getHostAddress(),
426 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800427
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700428 sendArpReply(arp, sw.getId(), pi.getInPort(),
429 configService.getRouterMacAddress());
430 }
pingping-linb8757bf2013-12-13 01:48:58 +0800431
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700432 return;
433 }
pingping-linb8757bf2013-12-13 01:48:58 +0800434
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700435 // MACAddress macAddress = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800436
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700437 arpRequests.put(
438 target,
439 new ArpRequest(new HostArpRequester(arp, sw.getId(), pi
440 .getInPort()), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800441
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700442 // TODO: Fix the code below after deviceStorage was removed
443 /*
444 IDeviceObject targetDevice =
445 deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
446 */
pingping-linb8757bf2013-12-13 01:48:58 +0800447
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700448 // TODO: Fix the code below after deviceStorage was removed
449 /*
450 if (targetDevice == null) {
451 if (log.isTraceEnabled()) {
452 log.trace("No device info found for {} - broadcasting",
453 target.getHostAddress());
454 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800455
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700456 // We don't know the device so broadcast the request out
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700457 PacketOutNotification key =
458 new BroadcastPacketOutNotification(eth.serialize(),
459 target, sw.getId(),
460 pi.getInPort());
461 packetOutEventChannel.addTransientEntry(key, key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700462 }
463 else {
464 // Even if the device exists in our database, we do not reply to
465 // the request directly, but check whether the device is still valid
466 MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200467
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700468 if (log.isTraceEnabled()) {
469 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
470 new Object [] {
471 inetAddressToString(arp.getTargetProtocolAddress()),
472 macAddress,
473 HexString.toHexString(sw.getId()), pi.getInPort()});
474 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200475
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700476 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800477
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700478 Iterable<IPortObject> outPorts = targetDevice.getAttachedPorts();
Pavlin Radoslavov5fe71882014-03-21 16:27:21 -0700479
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700480 if (!outPorts.iterator().hasNext()){
481 if (log.isTraceEnabled()) {
482 log.trace("Device {} exists but is not connected to any ports" +
483 " - broadcasting", macAddress);
484 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700485 PacketOutNotification key =
486 new BroadcastPacketOutNotification(eth.serialize(),
487 target, sw.getId(),
488 pi.getInPort());
489 packetOutEventChannel.addTransientEntry(key, key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700490 }
491 else {
492 for (IPortObject portObject : outPorts) {
493 //long outSwitch = 0;
494 //short outPort = 0;
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200495
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700496 // if (!portObject.getLinkedPorts().iterator().hasNext()) {
497 // outPort = portObject.getNumber();
498 // }
499 if (portObject.getLinkedPorts().iterator().hasNext()) {
500 continue;
501 }
Jonathan Hartc824ad02013-07-03 15:58:45 +1200502
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700503 short outPort = portObject.getNumber();
504 ISwitchObject outSwitchObject = portObject.getSwitch();
505 long outSwitch = HexString.toLong(outSwitchObject.getDPID());
Jonathan Hart4dfc3652013-08-02 20:22:36 +1200506
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700507 if (log.isTraceEnabled()) {
508 log.trace("Probing device {} on port {}/{}",
509 new Object[] {macAddress,
510 HexString.toHexString(outSwitch), outPort});
511 }
Jonathan Hart18ad55c2013-11-11 22:49:55 -0800512
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700513 PacketOutNotification key =
514 new SinglePacketOutNotification(eth.serialize(),
515 target, outSwitch, outPort);
516 packetOutEventChannel.addTransientEntry(key, key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700517 }
518 }
519 }
520 */
521 }
pingping-linb8757bf2013-12-13 01:48:58 +0800522
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700523 // Not used because device manager currently updates the database
524 // for ARP replies. May be useful in the future.
525 private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp) {
526 if (log.isTraceEnabled()) {
527 log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
528 inetAddressToString(arp.getSenderProtocolAddress()),
529 HexString.toHexString(arp.getSenderHardwareAddress()),
530 HexString.toHexString(sw.getId()), pi.getInPort()});
531 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800532
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700533 InetAddress senderIpAddress;
534 try {
535 senderIpAddress = InetAddress.getByAddress(arp
536 .getSenderProtocolAddress());
537 } catch (UnknownHostException e) {
538 log.debug("Invalid address in ARP reply", e);
539 return;
540 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800541
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700542 MACAddress senderMacAddress = MACAddress.valueOf(arp
543 .getSenderHardwareAddress());
544
545 // See if anyone's waiting for this ARP reply
546 Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
547
548 // Synchronize on the Multimap while using an iterator for one of the
549 // sets
550 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
551 requests.size());
552 synchronized (arpRequests) {
553 Iterator<ArpRequest> it = requests.iterator();
554 while (it.hasNext()) {
555 ArpRequest request = it.next();
556 it.remove();
557 requestsToSend.add(request);
558 }
559 }
560
561 // Don't hold an ARP lock while dispatching requests
562 for (ArpRequest request : requestsToSend) {
563 request.dispatchReply(senderIpAddress, senderMacAddress);
564 }
565 }
566
567 private void sendArpRequestForAddress(InetAddress ipAddress) {
568 // TODO what should the sender IP address and MAC address be if no
569 // IP addresses are configured? Will there ever be a need to send
570 // ARP requests from the controller in that case?
571 // All-zero MAC address doesn't seem to work - hosts don't respond to it
572
573 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
574 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
575 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
576 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
577 (byte) 0xff, (byte) 0xff, (byte) 0xff};
578
579 ARP arpRequest = new ARP();
580
581 arpRequest
582 .setHardwareType(ARP.HW_TYPE_ETHERNET)
583 .setProtocolType(ARP.PROTO_TYPE_IP)
584 .setHardwareAddressLength(
585 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
586 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
587 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
588 .setTargetProtocolAddress(ipAddress.getAddress());
589
590 MACAddress routerMacAddress = configService.getRouterMacAddress();
591 // TODO hack for now as it's unclear what the MAC address should be
592 byte[] senderMacAddress = genericNonZeroMac;
593 if (routerMacAddress != null) {
594 senderMacAddress = routerMacAddress.toBytes();
595 }
596 arpRequest.setSenderHardwareAddress(senderMacAddress);
597
598 byte[] senderIPAddress = zeroIpv4;
599 Interface intf = configService.getOutgoingInterface(ipAddress);
600 if (intf != null) {
601 senderIPAddress = intf.getIpAddress().getAddress();
602 }
603
604 arpRequest.setSenderProtocolAddress(senderIPAddress);
605
606 Ethernet eth = new Ethernet();
607 eth.setSourceMACAddress(senderMacAddress)
608 .setDestinationMACAddress(broadcastMac)
609 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
610
611 if (vlan != NO_VLAN) {
612 eth.setVlanID(vlan).setPriorityCode((byte) 0);
613 }
614
615 // sendArpRequestToSwitches(ipAddress, eth.serialize());
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700616 PacketOutNotification key =
617 new SinglePacketOutNotification(eth.serialize(), ipAddress,
618 intf.getDpid(), intf.getPort());
619 packetOutEventChannel.addTransientEntry(key, key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700620 }
621
622 private void sendArpRequestToSwitches(InetAddress dstAddress,
623 byte[] arpRequest) {
624 sendArpRequestToSwitches(dstAddress, arpRequest, 0,
625 OFPort.OFPP_NONE.getValue());
626 }
627
628 private void sendArpRequestToSwitches(InetAddress dstAddress,
629 byte[] arpRequest, long inSwitch, short inPort) {
630
631 if (configService.hasLayer3Configuration()) {
632 Interface intf = configService.getOutgoingInterface(dstAddress);
633 if (intf == null) {
634 // TODO here it should be broadcast out all non-interface edge
635 // ports.
636 // I think we can assume that if it's not a request for an
637 // external
638 // network, it's an ARP for a host in our own network. So we
639 // want to
640 // send it out all edge ports that don't have an interface
641 // configured
642 // to ensure it reaches all hosts in our network.
643 log.debug("No interface found to send ARP request for {}",
644 dstAddress.getHostAddress());
645 } else {
646 sendArpRequestOutPort(arpRequest, intf.getDpid(),
647 intf.getPort());
648 }
649 } else {
650 // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
651 broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
652 }
653 }
654
655 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
656 ARP arp = (ARP) eth.getPayload();
657
658 if (log.isTraceEnabled()) {
659 log.trace("Sending ARP reply for {} to other ONOS instances",
660 inetAddressToString(arp.getSenderProtocolAddress()));
661 }
662
663 InetAddress targetAddress;
664
665 try {
666 targetAddress = InetAddress.getByAddress(arp
667 .getSenderProtocolAddress());
668 } catch (UnknownHostException e) {
669 log.error("Unknown host", e);
670 return;
671 }
672
673 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
674
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700675 ArpReplyNotification key =
676 new ArpReplyNotification(targetAddress, mac);
677 arpReplyEventChannel.addTransientEntry(key, key);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700678 }
679
680 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
681 short inPort) {
682 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
683
684 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
685
686 OFPacketOut po = new OFPacketOut();
687 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
688 .setPacketData(arpRequest);
689
690 List<OFAction> actions = new ArrayList<OFAction>();
691
692 // TODO: Fix the code below after topoSwitchService was removed
693 /*
694 Iterable<IPortObject> ports
695 = topoSwitchService.getPortsOnSwitch(sw.getStringId());
696 if (ports == null) {
697 continue;
698 }
699
700 for (IPortObject portObject : ports) {
701 if (!portObject.getLinkedPorts().iterator().hasNext()) {
702 short portNumber = portObject.getNumber();
703
704 if (sw.getId() == inSwitch && portNumber == inPort) {
705 // This is the port that the ARP message came in,
706 // so don't broadcast out this port
707 continue;
708 }
709
710 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
711 new Port(portNumber)));
712 actions.add(new OFActionOutput(portNumber));
713 }
714 }
715 */
716
717 po.setActions(actions);
718 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
719 po.setActionsLength(actionsLength);
720 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
721 + arpRequest.length);
722
723 flowPusher.add(sw, po);
724 }
725
726 if (log.isTraceEnabled()) {
727 log.trace("Broadcast ARP request to: {}", switchPorts);
728 }
729 }
730
731 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
732 if (log.isTraceEnabled()) {
733 log.trace("Sending ARP request out {}/{}",
734 HexString.toHexString(dpid), port);
735 }
736
737 OFPacketOut po = new OFPacketOut();
738 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
739 .setPacketData(arpRequest);
740
741 List<OFAction> actions = new ArrayList<OFAction>();
742 actions.add(new OFActionOutput(port));
743 po.setActions(actions);
744 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
745 po.setActionsLength(actionsLength);
746 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
747 + arpRequest.length);
748
749 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
750
751 if (sw == null) {
752 log.warn("Switch not found when sending ARP request");
753 return;
754 }
755
756 flowPusher.add(sw, po);
757 }
758
759 private void sendArpReply(ARP arpRequest, long dpid, short port,
760 MACAddress targetMac) {
761 if (log.isTraceEnabled()) {
762 log.trace(
763 "Sending reply {} => {} to {}",
764 new Object[] {
765 inetAddressToString(arpRequest
766 .getTargetProtocolAddress()),
767 targetMac,
768 inetAddressToString(arpRequest
769 .getSenderProtocolAddress())});
770 }
771
772 ARP arpReply = new ARP();
773 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
774 .setProtocolType(ARP.PROTO_TYPE_IP)
775 .setHardwareAddressLength(
776 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
777 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
778 .setOpCode(ARP.OP_REPLY)
779 .setSenderHardwareAddress(targetMac.toBytes())
780 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
781 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
782 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
783
784 Ethernet eth = new Ethernet();
785 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
786 .setSourceMACAddress(targetMac.toBytes())
787 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
788
789 if (vlan != NO_VLAN) {
790 eth.setVlanID(vlan).setPriorityCode((byte) 0);
791 }
792
793 List<OFAction> actions = new ArrayList<OFAction>();
794 actions.add(new OFActionOutput(port));
795
796 OFPacketOut po = new OFPacketOut();
797 po.setInPort(OFPort.OFPP_NONE)
798 .setBufferId(-1)
799 .setPacketData(eth.serialize())
800 .setActions(actions)
801 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
802 .setLengthU(
803 OFPacketOut.MINIMUM_LENGTH
804 + OFActionOutput.MINIMUM_LENGTH
805 + po.getPacketData().length);
806
807 List<OFMessage> msgList = new ArrayList<OFMessage>();
808 msgList.add(po);
809
810 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
811
812 if (sw == null) {
813 log.warn("Switch {} not found when sending ARP reply",
814 HexString.toHexString(dpid));
815 return;
816 }
817
818 flowPusher.add(sw, po);
819 }
820
821 private String inetAddressToString(byte[] bytes) {
822 try {
823 return InetAddress.getByAddress(bytes).getHostAddress();
824 } catch (UnknownHostException e) {
825 log.debug("Invalid IP address", e);
826 return "";
827 }
828 }
829
830 /*
831 * IProxyArpService methods
832 */
833
834 @Override
835 public MACAddress getMacAddress(InetAddress ipAddress) {
836 // return arpCache.lookup(ipAddress);
837 return null;
838 }
839
840 @Override
841 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
842 boolean retry) {
843 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
844
845 // Sanity check to make sure we don't send a request for our own address
846 if (!configService.isInterfaceAddress(ipAddress)) {
847 sendArpRequestForAddress(ipAddress);
848 }
849 }
850
851 @Override
852 public List<String> getMappings() {
853 return new ArrayList<String>();
854 }
855
856 /*
857 @Override
858 public void arpRequestNotification(ArpMessage arpMessage) {
859 log.debug("Received ARP notification from other instances");
860
861 switch (arpMessage.getType()){
862 case REQUEST:
863 if(arpMessage.getOutSwitch() == -1 || arpMessage.getOutPort() == -1){
864 broadcastArpRequestOutMyEdge(arpMessage.getPacket(),
865 arpMessage.getInSwitch(), arpMessage.getInPort());
866 }else{
867 sendArpRequestOutPort(arpMessage.getPacket(),arpMessage.getOutSwitch(),arpMessage.getOutPort());
868 log.debug("OutSwitch in ARP request message is: {}; " +
869 "OutPort in ARP request message is: {}",arpMessage.getOutSwitch(),arpMessage.getOutPort());
870 }
871 break;
872 case REPLY:
873 log.debug("Received ARP reply notification for {}",
874 arpMessage.getAddress());
875 sendArpReplyToWaitingRequesters(arpMessage.getAddress(),arpMessage.getMAC());
876 break;
877 }
878 }
879 */
880
881 private void sendArpReplyToWaitingRequesters(InetAddress address,
882 MACAddress mac) {
883 log.debug("Sending ARP reply for {} to requesters",
884 address.getHostAddress());
885
886 // See if anyone's waiting for this ARP reply
887 Set<ArpRequest> requests = arpRequests.get(address);
888
889 // Synchronize on the Multimap while using an iterator for one of the
890 // sets
891 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
892 requests.size());
893 synchronized (arpRequests) {
894 Iterator<ArpRequest> it = requests.iterator();
895 while (it.hasNext()) {
896 ArpRequest request = it.next();
897 it.remove();
898 requestsToSend.add(request);
899 }
900 }
901
902 /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
903 InetAddresses.coerceToInteger(address));
904
905 MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
906
907 log.debug("Found {} at {} in network map",
908 address.getHostAddress(), mac);*/
909
910 // Don't hold an ARP lock while dispatching requests
911 for (ArpRequest request : requestsToSend) {
912 request.dispatchReply(address, mac);
913 }
914 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200915}