blob: 33d4d7d9a6654f96644a05b60cda10240b3ec41c [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;
Jonathan Hart2f790d22013-08-15 14:01:24 +120025import net.onrc.onos.ofcontroller.bgproute.Interface;
Jonathan Hartebba1e12013-10-29 11:37:02 -070026import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
Jonathan Hart7804bea2014-01-07 10:50:52 -080027import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
Jonathan Hartba9ced92013-11-24 16:52:13 -080028import net.onrc.onos.ofcontroller.util.SwitchPort;
Jonathan Hart96892d12014-03-26 20:21:29 -070029import net.onrc.onos.packet.ARP;
30import net.onrc.onos.packet.Ethernet;
31import net.onrc.onos.packet.IPv4;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120032
33import org.openflow.protocol.OFMessage;
34import org.openflow.protocol.OFPacketIn;
35import org.openflow.protocol.OFPacketOut;
36import org.openflow.protocol.OFPort;
37import org.openflow.protocol.OFType;
38import org.openflow.protocol.action.OFAction;
39import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120040import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
Jonathan Hart4dfc3652013-08-02 20:22:36 +120044import com.google.common.collect.HashMultimap;
45import com.google.common.collect.Multimaps;
46import com.google.common.collect.SetMultimap;
47
Jonathan Hart18ad55c2013-11-11 22:49:55 -080048public class ProxyArpManager implements IProxyArpService, IOFMessageListener,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070049 IPacketOutEventHandler, IArpReplyEventHandler, IFloodlightModule {
50 private static final Logger log = LoggerFactory
51 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080052
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070053 private static final long ARP_TIMER_PERIOD = 100; // ms
Jonathan Hartdf6ec332013-08-04 01:37:14 +120054
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070055 private static final int ARP_REQUEST_TIMEOUT = 2000; // ms
Jonathan Hartda4d0e12013-09-30 21:00:20 +130056
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070057 private IFloodlightProviderService floodlightProvider;
58 private IDatagridService datagrid;
59 private IConfigInfoService configService;
60 private IRestApiService restApi;
61 private IFlowPusherService flowPusher;
Jonathan Harte93aed42013-12-05 18:39:50 -080062
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070063 private short vlan;
64 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080065
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070066 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120067
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070068 private static class ArpRequest {
69 private final IArpRequester requester;
70 private final boolean retry;
71 private boolean sent = false;
72 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120073
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070074 public ArpRequest(IArpRequester requester, boolean retry) {
75 this.requester = requester;
76 this.retry = retry;
77 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120078
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070079 public ArpRequest(ArpRequest old) {
80 this.requester = old.requester;
81 this.retry = old.retry;
82 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120083
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070084 public boolean isExpired() {
85 return sent
86 && ((System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT);
87 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120088
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070089 public boolean shouldRetry() {
90 return retry;
91 }
92
93 public void dispatchReply(InetAddress ipAddress,
94 MACAddress replyMacAddress) {
95 requester.arpResponse(ipAddress, replyMacAddress);
96 }
97
98 public void setRequestTime() {
99 this.requestTime = System.currentTimeMillis();
100 this.sent = true;
101 }
102 }
103
104 private class HostArpRequester implements IArpRequester {
105 private final ARP arpRequest;
106 private final long dpid;
107 private final short port;
108
109 public HostArpRequester(ARP arpRequest, long dpid, short port) {
110 this.arpRequest = arpRequest;
111 this.dpid = dpid;
112 this.port = port;
113 }
114
115 @Override
116 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
117 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
118 macAddress);
119 }
120 }
121
122 @Override
123 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
124 Collection<Class<? extends IFloodlightService>> l =
125 new ArrayList<Class<? extends IFloodlightService>>();
126 l.add(IProxyArpService.class);
127 return l;
128 }
129
130 @Override
131 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
132 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
133 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
134 m.put(IProxyArpService.class, this);
135 return m;
136 }
137
138 @Override
139 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
140 Collection<Class<? extends IFloodlightService>> dependencies =
141 new ArrayList<Class<? extends IFloodlightService>>();
142 dependencies.add(IFloodlightProviderService.class);
143 dependencies.add(IRestApiService.class);
144 dependencies.add(IDatagridService.class);
145 dependencies.add(IConfigInfoService.class);
146 dependencies.add(IFlowPusherService.class);
147 return dependencies;
148 }
149
150 @Override
151 public void init(FloodlightModuleContext context) {
152 this.floodlightProvider = context
153 .getServiceImpl(IFloodlightProviderService.class);
154 this.datagrid = context.getServiceImpl(IDatagridService.class);
155 this.configService = context.getServiceImpl(IConfigInfoService.class);
156 this.restApi = context.getServiceImpl(IRestApiService.class);
157 this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
158
159 // arpCache = new ArpCache();
160
161 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
162 .<InetAddress, ArpRequest>create());
163
164 }
165
166 @Override
167 public void startUp(FloodlightModuleContext context) {
168 this.vlan = configService.getVlan();
169 log.info("vlan set to {}", this.vlan);
170
171 restApi.addRestletRoutable(new ArpWebRoutable());
172 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
173
174 datagrid.registerPacketOutEventHandler(this);
175 datagrid.registerArpReplyEventHandler(this);
176
177 Timer arpTimer = new Timer("arp-processing");
178 arpTimer.scheduleAtFixedRate(new TimerTask() {
179 @Override
180 public void run() {
181 doPeriodicArpProcessing();
182 }
183 }, 0, ARP_TIMER_PERIOD);
184 }
185
186 /*
187 * Function that runs periodically to manage the asynchronous request mechanism.
188 * It basically cleans up old ARP requests if we don't get a response for them.
189 * The caller can designate that a request should be retried indefinitely, and
190 * this task will handle that as well.
191 */
192 private void doPeriodicArpProcessing() {
193 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
194 .<InetAddress, ArpRequest>create();
195
196 // Have to synchronize externally on the Multimap while using an
197 // iterator,
198 // even though it's a synchronizedMultimap
199 synchronized (arpRequests) {
200 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
201 .entries().iterator();
202
203 while (it.hasNext()) {
204 Map.Entry<InetAddress, ArpRequest> entry = it.next();
205 ArpRequest request = entry.getValue();
206 if (request.isExpired()) {
207 log.debug("Cleaning expired ARP request for {}", entry
208 .getKey().getHostAddress());
209
210 // If the ARP request is expired and then delete the device
211 // TODO check whether this is OK from this thread
212 // TODO: Fix the code below after deviceStorage was removed
213 /*
214 IDeviceObject targetDevice =
215 deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(entry.getKey()));
216 if (targetDevice != null) {
217 deviceStorage.removeDevice(targetDevice);
218 if (log.isDebugEnabled()) {
219 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDevice);
220 }
221 }
222 */
223
224 it.remove();
225
226 if (request.shouldRetry()) {
227 retryList.put(entry.getKey(), request);
228 }
229 }
230 }
231 }
232
233 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
234 .asMap().entrySet()) {
235
236 InetAddress address = entry.getKey();
237
238 log.debug("Resending ARP request for {}", address.getHostAddress());
239
240 // Only ARP requests sent by the controller will have the retry flag
241 // set, so for now we can just send a new ARP request for that
242 // address.
243 sendArpRequestForAddress(address);
244
245 for (ArpRequest request : entry.getValue()) {
246 arpRequests.put(address, new ArpRequest(request));
247 }
248 }
249 }
250
251 @Override
252 public String getName() {
253 return "proxyarpmanager";
254 }
255
256 @Override
257 public boolean isCallbackOrderingPrereq(OFType type, String name) {
258 if (type == OFType.PACKET_IN) {
259 return "devicemanager".equals(name)
260 || "onosdevicemanager".equals(name);
261 } else {
262 return false;
263 }
264 }
265
266 @Override
267 public boolean isCallbackOrderingPostreq(OFType type, String name) {
268 return type == OFType.PACKET_IN && "onosforwarding".equals(name);
269 }
270
271 @Override
272 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
273
274 OFPacketIn pi = (OFPacketIn) msg;
275
276 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200277 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Jonathan Hart2f790d22013-08-15 14:01:24 +1200278
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700279 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
280 ARP arp = (ARP) eth.getPayload();
281 if (arp.getOpCode() == ARP.OP_REQUEST) {
282 handleArpRequest(sw, pi, arp, eth);
283 } else if (arp.getOpCode() == ARP.OP_REPLY) {
284 // For replies we simply send a notification via Hazelcast
285 sendArpReplyNotification(eth, pi);
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200286
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700287 // handleArpReply(sw, pi, arp);
288 }
pingping-linb8757bf2013-12-13 01:48:58 +0800289
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700290 // Stop ARP packets here
291 return Command.STOP;
292 }
pingping-linb8757bf2013-12-13 01:48:58 +0800293
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700294 // Propagate everything else
295 return Command.CONTINUE;
296 }
pingping-linb8757bf2013-12-13 01:48:58 +0800297
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700298 private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
299 Ethernet eth) {
300 if (log.isTraceEnabled()) {
301 log.trace("ARP request received for {}",
302 inetAddressToString(arp.getTargetProtocolAddress()));
303 }
pingping-linb8757bf2013-12-13 01:48:58 +0800304
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700305 InetAddress target;
306 try {
307 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
308 } catch (UnknownHostException e) {
309 log.debug("Invalid address in ARP request", e);
310 return;
311 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700312
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700313 if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
314 // If the request came from outside our network, we only care if
315 // it was a request for one of our interfaces.
316 if (configService.isInterfaceAddress(target)) {
317 log.trace(
318 "ARP request for our interface. Sending reply {} => {}",
319 target.getHostAddress(),
320 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800321
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700322 sendArpReply(arp, sw.getId(), pi.getInPort(),
323 configService.getRouterMacAddress());
324 }
pingping-linb8757bf2013-12-13 01:48:58 +0800325
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700326 return;
327 }
pingping-linb8757bf2013-12-13 01:48:58 +0800328
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700329 // MACAddress macAddress = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800330
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700331 arpRequests.put(
332 target,
333 new ArpRequest(new HostArpRequester(arp, sw.getId(), pi
334 .getInPort()), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800335
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700336 // TODO: Fix the code below after deviceStorage was removed
337 /*
338 IDeviceObject targetDevice =
339 deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
340 */
pingping-linb8757bf2013-12-13 01:48:58 +0800341
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700342 // TODO: Fix the code below after deviceStorage was removed
343 /*
344 if (targetDevice == null) {
345 if (log.isTraceEnabled()) {
346 log.trace("No device info found for {} - broadcasting",
347 target.getHostAddress());
348 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800349
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700350 // We don't know the device so broadcast the request out
351 datagrid.sendPacketOutNotification(
352 new BroadcastPacketOutNotification(eth.serialize(),
353 target, sw.getId(), pi.getInPort()));
354 }
355 else {
356 // Even if the device exists in our database, we do not reply to
357 // the request directly, but check whether the device is still valid
358 MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200359
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700360 if (log.isTraceEnabled()) {
361 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
362 new Object [] {
363 inetAddressToString(arp.getTargetProtocolAddress()),
364 macAddress,
365 HexString.toHexString(sw.getId()), pi.getInPort()});
366 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200367
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700368 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800369
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700370 Iterable<IPortObject> outPorts = targetDevice.getAttachedPorts();
Pavlin Radoslavov5fe71882014-03-21 16:27:21 -0700371
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700372 if (!outPorts.iterator().hasNext()){
373 if (log.isTraceEnabled()) {
374 log.trace("Device {} exists but is not connected to any ports" +
375 " - broadcasting", macAddress);
376 }
Jonathan Hart1cf9de02013-10-21 17:42:29 -0700377
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700378 datagrid.sendPacketOutNotification(
379 new BroadcastPacketOutNotification(eth.serialize(),
380 target, sw.getId(), pi.getInPort()));
381 }
382 else {
383 for (IPortObject portObject : outPorts) {
384 //long outSwitch = 0;
385 //short outPort = 0;
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200386
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700387 // if (!portObject.getLinkedPorts().iterator().hasNext()) {
388 // outPort = portObject.getNumber();
389 // }
390 if (portObject.getLinkedPorts().iterator().hasNext()) {
391 continue;
392 }
Jonathan Hartc824ad02013-07-03 15:58:45 +1200393
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700394 short outPort = portObject.getNumber();
395 ISwitchObject outSwitchObject = portObject.getSwitch();
396 long outSwitch = HexString.toLong(outSwitchObject.getDPID());
Jonathan Hart4dfc3652013-08-02 20:22:36 +1200397
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700398 if (log.isTraceEnabled()) {
399 log.trace("Probing device {} on port {}/{}",
400 new Object[] {macAddress,
401 HexString.toHexString(outSwitch), outPort});
402 }
Jonathan Hart18ad55c2013-11-11 22:49:55 -0800403
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700404 datagrid.sendPacketOutNotification(
405 new SinglePacketOutNotification(eth.serialize(),
406 target, outSwitch, outPort));
407 }
408 }
409 }
410 */
411 }
pingping-linb8757bf2013-12-13 01:48:58 +0800412
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700413 // Not used because device manager currently updates the database
414 // for ARP replies. May be useful in the future.
415 private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp) {
416 if (log.isTraceEnabled()) {
417 log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
418 inetAddressToString(arp.getSenderProtocolAddress()),
419 HexString.toHexString(arp.getSenderHardwareAddress()),
420 HexString.toHexString(sw.getId()), pi.getInPort()});
421 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800422
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700423 InetAddress senderIpAddress;
424 try {
425 senderIpAddress = InetAddress.getByAddress(arp
426 .getSenderProtocolAddress());
427 } catch (UnknownHostException e) {
428 log.debug("Invalid address in ARP reply", e);
429 return;
430 }
Jonathan Hart7804bea2014-01-07 10:50:52 -0800431
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700432 MACAddress senderMacAddress = MACAddress.valueOf(arp
433 .getSenderHardwareAddress());
434
435 // See if anyone's waiting for this ARP reply
436 Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
437
438 // Synchronize on the Multimap while using an iterator for one of the
439 // sets
440 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
441 requests.size());
442 synchronized (arpRequests) {
443 Iterator<ArpRequest> it = requests.iterator();
444 while (it.hasNext()) {
445 ArpRequest request = it.next();
446 it.remove();
447 requestsToSend.add(request);
448 }
449 }
450
451 // Don't hold an ARP lock while dispatching requests
452 for (ArpRequest request : requestsToSend) {
453 request.dispatchReply(senderIpAddress, senderMacAddress);
454 }
455 }
456
457 private void sendArpRequestForAddress(InetAddress ipAddress) {
458 // TODO what should the sender IP address and MAC address be if no
459 // IP addresses are configured? Will there ever be a need to send
460 // ARP requests from the controller in that case?
461 // All-zero MAC address doesn't seem to work - hosts don't respond to it
462
463 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
464 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
465 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
466 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
467 (byte) 0xff, (byte) 0xff, (byte) 0xff};
468
469 ARP arpRequest = new ARP();
470
471 arpRequest
472 .setHardwareType(ARP.HW_TYPE_ETHERNET)
473 .setProtocolType(ARP.PROTO_TYPE_IP)
474 .setHardwareAddressLength(
475 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
476 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
477 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
478 .setTargetProtocolAddress(ipAddress.getAddress());
479
480 MACAddress routerMacAddress = configService.getRouterMacAddress();
481 // TODO hack for now as it's unclear what the MAC address should be
482 byte[] senderMacAddress = genericNonZeroMac;
483 if (routerMacAddress != null) {
484 senderMacAddress = routerMacAddress.toBytes();
485 }
486 arpRequest.setSenderHardwareAddress(senderMacAddress);
487
488 byte[] senderIPAddress = zeroIpv4;
489 Interface intf = configService.getOutgoingInterface(ipAddress);
490 if (intf != null) {
491 senderIPAddress = intf.getIpAddress().getAddress();
492 }
493
494 arpRequest.setSenderProtocolAddress(senderIPAddress);
495
496 Ethernet eth = new Ethernet();
497 eth.setSourceMACAddress(senderMacAddress)
498 .setDestinationMACAddress(broadcastMac)
499 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
500
501 if (vlan != NO_VLAN) {
502 eth.setVlanID(vlan).setPriorityCode((byte) 0);
503 }
504
505 // sendArpRequestToSwitches(ipAddress, eth.serialize());
506 datagrid.sendPacketOutNotification(new SinglePacketOutNotification(eth
507 .serialize(), ipAddress, intf.getDpid(), intf.getPort()));
508 }
509
510 private void sendArpRequestToSwitches(InetAddress dstAddress,
511 byte[] arpRequest) {
512 sendArpRequestToSwitches(dstAddress, arpRequest, 0,
513 OFPort.OFPP_NONE.getValue());
514 }
515
516 private void sendArpRequestToSwitches(InetAddress dstAddress,
517 byte[] arpRequest, long inSwitch, short inPort) {
518
519 if (configService.hasLayer3Configuration()) {
520 Interface intf = configService.getOutgoingInterface(dstAddress);
521 if (intf == null) {
522 // TODO here it should be broadcast out all non-interface edge
523 // ports.
524 // I think we can assume that if it's not a request for an
525 // external
526 // network, it's an ARP for a host in our own network. So we
527 // want to
528 // send it out all edge ports that don't have an interface
529 // configured
530 // to ensure it reaches all hosts in our network.
531 log.debug("No interface found to send ARP request for {}",
532 dstAddress.getHostAddress());
533 } else {
534 sendArpRequestOutPort(arpRequest, intf.getDpid(),
535 intf.getPort());
536 }
537 } else {
538 // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
539 broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
540 }
541 }
542
543 private void sendArpReplyNotification(Ethernet eth, OFPacketIn pi) {
544 ARP arp = (ARP) eth.getPayload();
545
546 if (log.isTraceEnabled()) {
547 log.trace("Sending ARP reply for {} to other ONOS instances",
548 inetAddressToString(arp.getSenderProtocolAddress()));
549 }
550
551 InetAddress targetAddress;
552
553 try {
554 targetAddress = InetAddress.getByAddress(arp
555 .getSenderProtocolAddress());
556 } catch (UnknownHostException e) {
557 log.error("Unknown host", e);
558 return;
559 }
560
561 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
562
563 datagrid.sendArpReplyNotification(new ArpReplyNotification(
564 targetAddress, mac));
565 }
566
567 private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
568 short inPort) {
569 List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
570
571 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
572
573 OFPacketOut po = new OFPacketOut();
574 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
575 .setPacketData(arpRequest);
576
577 List<OFAction> actions = new ArrayList<OFAction>();
578
579 // TODO: Fix the code below after topoSwitchService was removed
580 /*
581 Iterable<IPortObject> ports
582 = topoSwitchService.getPortsOnSwitch(sw.getStringId());
583 if (ports == null) {
584 continue;
585 }
586
587 for (IPortObject portObject : ports) {
588 if (!portObject.getLinkedPorts().iterator().hasNext()) {
589 short portNumber = portObject.getNumber();
590
591 if (sw.getId() == inSwitch && portNumber == inPort) {
592 // This is the port that the ARP message came in,
593 // so don't broadcast out this port
594 continue;
595 }
596
597 switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
598 new Port(portNumber)));
599 actions.add(new OFActionOutput(portNumber));
600 }
601 }
602 */
603
604 po.setActions(actions);
605 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
606 po.setActionsLength(actionsLength);
607 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
608 + arpRequest.length);
609
610 flowPusher.add(sw, po);
611 }
612
613 if (log.isTraceEnabled()) {
614 log.trace("Broadcast ARP request to: {}", switchPorts);
615 }
616 }
617
618 private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
619 if (log.isTraceEnabled()) {
620 log.trace("Sending ARP request out {}/{}",
621 HexString.toHexString(dpid), port);
622 }
623
624 OFPacketOut po = new OFPacketOut();
625 po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
626 .setPacketData(arpRequest);
627
628 List<OFAction> actions = new ArrayList<OFAction>();
629 actions.add(new OFActionOutput(port));
630 po.setActions(actions);
631 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
632 po.setActionsLength(actionsLength);
633 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
634 + arpRequest.length);
635
636 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
637
638 if (sw == null) {
639 log.warn("Switch not found when sending ARP request");
640 return;
641 }
642
643 flowPusher.add(sw, po);
644 }
645
646 private void sendArpReply(ARP arpRequest, long dpid, short port,
647 MACAddress targetMac) {
648 if (log.isTraceEnabled()) {
649 log.trace(
650 "Sending reply {} => {} to {}",
651 new Object[] {
652 inetAddressToString(arpRequest
653 .getTargetProtocolAddress()),
654 targetMac,
655 inetAddressToString(arpRequest
656 .getSenderProtocolAddress())});
657 }
658
659 ARP arpReply = new ARP();
660 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
661 .setProtocolType(ARP.PROTO_TYPE_IP)
662 .setHardwareAddressLength(
663 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
664 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
665 .setOpCode(ARP.OP_REPLY)
666 .setSenderHardwareAddress(targetMac.toBytes())
667 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
668 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
669 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
670
671 Ethernet eth = new Ethernet();
672 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
673 .setSourceMACAddress(targetMac.toBytes())
674 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
675
676 if (vlan != NO_VLAN) {
677 eth.setVlanID(vlan).setPriorityCode((byte) 0);
678 }
679
680 List<OFAction> actions = new ArrayList<OFAction>();
681 actions.add(new OFActionOutput(port));
682
683 OFPacketOut po = new OFPacketOut();
684 po.setInPort(OFPort.OFPP_NONE)
685 .setBufferId(-1)
686 .setPacketData(eth.serialize())
687 .setActions(actions)
688 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
689 .setLengthU(
690 OFPacketOut.MINIMUM_LENGTH
691 + OFActionOutput.MINIMUM_LENGTH
692 + po.getPacketData().length);
693
694 List<OFMessage> msgList = new ArrayList<OFMessage>();
695 msgList.add(po);
696
697 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
698
699 if (sw == null) {
700 log.warn("Switch {} not found when sending ARP reply",
701 HexString.toHexString(dpid));
702 return;
703 }
704
705 flowPusher.add(sw, po);
706 }
707
708 private String inetAddressToString(byte[] bytes) {
709 try {
710 return InetAddress.getByAddress(bytes).getHostAddress();
711 } catch (UnknownHostException e) {
712 log.debug("Invalid IP address", e);
713 return "";
714 }
715 }
716
717 /*
718 * IProxyArpService methods
719 */
720
721 @Override
722 public MACAddress getMacAddress(InetAddress ipAddress) {
723 // return arpCache.lookup(ipAddress);
724 return null;
725 }
726
727 @Override
728 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
729 boolean retry) {
730 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
731
732 // Sanity check to make sure we don't send a request for our own address
733 if (!configService.isInterfaceAddress(ipAddress)) {
734 sendArpRequestForAddress(ipAddress);
735 }
736 }
737
738 @Override
739 public List<String> getMappings() {
740 return new ArrayList<String>();
741 }
742
743 /*
744 @Override
745 public void arpRequestNotification(ArpMessage arpMessage) {
746 log.debug("Received ARP notification from other instances");
747
748 switch (arpMessage.getType()){
749 case REQUEST:
750 if(arpMessage.getOutSwitch() == -1 || arpMessage.getOutPort() == -1){
751 broadcastArpRequestOutMyEdge(arpMessage.getPacket(),
752 arpMessage.getInSwitch(), arpMessage.getInPort());
753 }else{
754 sendArpRequestOutPort(arpMessage.getPacket(),arpMessage.getOutSwitch(),arpMessage.getOutPort());
755 log.debug("OutSwitch in ARP request message is: {}; " +
756 "OutPort in ARP request message is: {}",arpMessage.getOutSwitch(),arpMessage.getOutPort());
757 }
758 break;
759 case REPLY:
760 log.debug("Received ARP reply notification for {}",
761 arpMessage.getAddress());
762 sendArpReplyToWaitingRequesters(arpMessage.getAddress(),arpMessage.getMAC());
763 break;
764 }
765 }
766 */
767
768 private void sendArpReplyToWaitingRequesters(InetAddress address,
769 MACAddress mac) {
770 log.debug("Sending ARP reply for {} to requesters",
771 address.getHostAddress());
772
773 // See if anyone's waiting for this ARP reply
774 Set<ArpRequest> requests = arpRequests.get(address);
775
776 // Synchronize on the Multimap while using an iterator for one of the
777 // sets
778 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
779 requests.size());
780 synchronized (arpRequests) {
781 Iterator<ArpRequest> it = requests.iterator();
782 while (it.hasNext()) {
783 ArpRequest request = it.next();
784 it.remove();
785 requestsToSend.add(request);
786 }
787 }
788
789 /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
790 InetAddresses.coerceToInteger(address));
791
792 MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
793
794 log.debug("Found {} at {} in network map",
795 address.getHostAddress(), mac);*/
796
797 // Don't hold an ARP lock while dispatching requests
798 for (ArpRequest request : requestsToSend) {
799 request.dispatchReply(address, mac);
800 }
801 }
802
803 @Override
804 public void arpReplyEvent(ArpReplyNotification arpReply) {
805 log.debug("Received ARP reply notification for {}",
806 arpReply.getTargetAddress());
807 sendArpReplyToWaitingRequesters(arpReply.getTargetAddress(),
808 arpReply.getTargetMacAddress());
809 }
810
811 @Override
812 public void packetOutNotification(
813 PacketOutNotification packetOutNotification) {
814
815 if (packetOutNotification instanceof SinglePacketOutNotification) {
816 SinglePacketOutNotification notification = (SinglePacketOutNotification) packetOutNotification;
817 sendArpRequestOutPort(notification.packet,
818 notification.getOutSwitch(), notification.getOutPort());
819
820 // set timestamp
821 InetAddress addr = notification.getTargetAddress();
822 if (addr != null) {
823 for (ArpRequest request : arpRequests.get(addr)) {
824 request.setRequestTime();
825 }
826 }
827 } else if (packetOutNotification instanceof BroadcastPacketOutNotification) {
828 BroadcastPacketOutNotification notification = (BroadcastPacketOutNotification) packetOutNotification;
829 broadcastArpRequestOutMyEdge(notification.packet,
830 notification.getInSwitch(), notification.getInPort());
831
832 // set timestamp
833 InetAddress addr = notification.getTargetAddress();
834 if (addr != null) {
835 for (ArpRequest request : arpRequests.get(addr)) {
836 request.setRequestTime();
837 }
838 }
839 } else {
840 log.warn("Unknown packet out notification received");
841 }
842 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200843}