blob: 5c2a2b9154d1f48c5b0708ea28aa7d3627f6965c [file] [log] [blame]
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12001package net.onrc.onos.ofcontroller.proxyarp;
2
3import java.io.IOException;
4import java.net.InetAddress;
5import java.net.UnknownHostException;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashMap;
Jonathan Hart6261dcd2013-07-22 17:58:35 +12009import java.util.HashSet;
10import java.util.Iterator;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120011import java.util.List;
12import java.util.Map;
13import java.util.Set;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120014import java.util.Timer;
15import java.util.TimerTask;
16import java.util.concurrent.ConcurrentHashMap;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120017
18import net.floodlightcontroller.core.FloodlightContext;
19import net.floodlightcontroller.core.IFloodlightProviderService;
20import net.floodlightcontroller.core.IOFMessageListener;
21import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120022import net.floodlightcontroller.devicemanager.IDeviceService;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120023import net.floodlightcontroller.packet.ARP;
24import net.floodlightcontroller.packet.Ethernet;
25import net.floodlightcontroller.topology.ITopologyService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120026import net.floodlightcontroller.util.MACAddress;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120027
28import org.openflow.protocol.OFMessage;
29import org.openflow.protocol.OFPacketIn;
30import org.openflow.protocol.OFPacketOut;
31import org.openflow.protocol.OFPort;
32import org.openflow.protocol.OFType;
33import org.openflow.protocol.action.OFAction;
34import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120035import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
Jonathan Hart6261dcd2013-07-22 17:58:35 +120039public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120040 private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
41
Jonathan Hart6261dcd2013-07-22 17:58:35 +120042 private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 10 mins)
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120043
Jonathan Hart6261dcd2013-07-22 17:58:35 +120044 private final long ARP_REQUEST_TIMEOUT_THREAD_PERIOD = 60000; //ms (== 1 min)
45
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120046 protected IFloodlightProviderService floodlightProvider;
47 protected ITopologyService topology;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120048 protected IDeviceService devices;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120049
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120050 protected Map<InetAddress, ArpTableEntry> arpTable;
51
Jonathan Hart6261dcd2013-07-22 17:58:35 +120052 //protected ConcurrentHashMap<InetAddress, Set<ArpRequest>> arpRequests;
53 protected ConcurrentHashMap<InetAddress, ArpRequest> arpRequests;
54
55 private class ArpRequest {
56 private Set<IArpRequester> requesters;
57 private long requestTime;
58
59 public ArpRequest(){
60 this.requesters = new HashSet<IArpRequester>();
61 this.requestTime = System.currentTimeMillis();
62 }
63
64 public synchronized void addRequester(IArpRequester requester){
65 requestTime = System.currentTimeMillis();
66 requesters.add(requester);
67 }
68
69 public boolean isExpired(){
70 return (System.currentTimeMillis() - requestTime)
71 > IProxyArpService.ARP_REQUEST_TIMEOUT;
72 }
73
74 public synchronized void dispatchReply(byte[] replyMacAddress){
75 for (IArpRequester requester : requesters){
76 requester.arpResponse(replyMacAddress);
77 }
78 }
79 }
80
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120081 public ProxyArpManager(IFloodlightProviderService floodlightProvider,
Jonathan Hart6261dcd2013-07-22 17:58:35 +120082 ITopologyService topology, IDeviceService devices){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120083 this.floodlightProvider = floodlightProvider;
84 this.topology = topology;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120085 this.devices = devices;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120086
87 arpTable = new HashMap<InetAddress, ArpTableEntry>();
Jonathan Hart6261dcd2013-07-22 17:58:35 +120088 //arpRequests = new ConcurrentHashMap<InetAddress, Set<ArpRequest>>();
89 arpRequests = new ConcurrentHashMap<InetAddress, ArpRequest>();
90
91 Timer arpRequestTimeoutTimer = new Timer();
92 arpRequestTimeoutTimer.scheduleAtFixedRate(new TimerTask() {
93 @Override
94 public void run() {
95 synchronized (arpRequests) {
96 log.debug("Current have {} outstanding requests",
97 arpRequests.size());
98
99 Iterator<Map.Entry<InetAddress, ArpRequest>> it
100 = arpRequests.entrySet().iterator();
101
102 while (it.hasNext()){
103 Map.Entry<InetAddress, ArpRequest> entry
104 = it.next();
105
106 if (entry.getValue().isExpired()){
107 log.debug("Cleaning expired ARP request for {}",
108 entry.getKey().getHostAddress());
109 it.remove();
110 }
111 }
112 }
113 }
114 }, 0, ARP_REQUEST_TIMEOUT_THREAD_PERIOD);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200115 }
116
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200117 private void storeRequester(InetAddress address, IArpRequester requester) {
118 synchronized (arpRequests) {
119 if (arpRequests.get(address) == null) {
120 arpRequests.put(address, new ArpRequest());
121 }
122 ArpRequest request = arpRequests.get(address);
123
124 request.addRequester(requester);
125 }
126 }
127
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200128 @Override
129 public String getName() {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200130 return "ProxyArpManager";
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200131 }
132
133 @Override
134 public boolean isCallbackOrderingPrereq(OFType type, String name) {
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200135 return false;
136 }
137
138 @Override
139 public boolean isCallbackOrderingPostreq(OFType type, String name) {
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200140 return false;
141 }
142
143 @Override
144 public Command receive(
145 IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
146
147 if (msg.getType() != OFType.PACKET_IN){
148 return Command.CONTINUE;
149 }
150
151 OFPacketIn pi = (OFPacketIn) msg;
152
153 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
154 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
155
156 if (eth.getEtherType() == Ethernet.TYPE_ARP){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200157 ARP arp = (ARP) eth.getPayload();
158
159 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200160 handleArpRequest(sw, pi, arp);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200161 }
162 else if (arp.getOpCode() == ARP.OP_REPLY) {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200163 handleArpReply(sw, pi, arp);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200164 }
165 }
166
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200167 //TODO should we propagate ARP or swallow it?
168 //Always propagate for now so DeviceManager can learn the host location
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200169 return Command.CONTINUE;
170 }
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200171
172 protected void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
173 log.debug("ARP request received for {}",
174 bytesToStringAddr(arp.getTargetProtocolAddress()));
175
176 byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
177
178 if (mac == null){
179 //Mac address is not in our arp table.
180
181 //TODO check what the DeviceManager thinks
182
183 //Record where the request came from so we know where to send the reply
184 InetAddress target;
185 try {
186 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
187 } catch (UnknownHostException e) {
188 log.debug("Invalid address in ARP request", e);
189 //return Command.CONTINUE; //Continue or stop?
190 return;
191 }
192
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200193 storeRequester(target, new HostArpRequester(this, arp, sw.getId(),
194 pi.getInPort()));
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200195
196 //Flood the request out edge ports
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200197 broadcastArpRequestOutEdge(pi.getPacketData(), sw.getId(), pi.getInPort());
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200198 }
199 else {
200 //We know the address, so send a reply
201 log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
202 //sendArpReply(arp, pi, mac, sw);
203 sendArpReply(arp, sw.getId(), pi.getInPort(), mac);
204 }
205 }
206
207 protected void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
208 log.debug("ARP reply recieved for {}",
209 bytesToStringAddr(arp.getSenderProtocolAddress()));
210
211 updateArpTable(arp);
212
213 //See if anyone's waiting for this ARP reply
214 InetAddress addr;
215 try {
216 addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
217 } catch (UnknownHostException e) {
218 return;
219 }
220
221 ArpRequest request = null;
222 synchronized (arpRequests) {
223 request = arpRequests.get(addr);
224 if (request != null) {
225 arpRequests.remove(addr);
226 }
227 }
228 if (request != null && !request.isExpired()) {
229 request.dispatchReply(arp.getSenderHardwareAddress());
230 }
231
232 /*
233 Set<ArpRequest> requests = arpRequests.get(addr);
234 if (requests != null){
235
236 synchronized (requests) {
237 for (ArpRequest request : requests) {
238 if (!request.isExpired()){
239 request.getRequester().arpResponse(
240 arp.getSenderHardwareAddress());
241 }
242 }
243 }
244 }*/
245 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200246
247 private synchronized byte[] lookupArpTable(byte[] ipAddress){
248 InetAddress addr;
249 try {
250 addr = InetAddress.getByAddress(ipAddress);
251 } catch (UnknownHostException e) {
252 log.warn("Unable to create InetAddress", e);
253 return null;
254 }
255
256 ArpTableEntry arpEntry = arpTable.get(addr);
257
258 if (arpEntry == null){
259 return null;
260 }
261
262 if (System.currentTimeMillis() - arpEntry.getTimeLastSeen()
263 > ARP_ENTRY_TIMEOUT){
264 //Entry has timed out so we'll remove it and return null
265 arpTable.remove(addr);
266 return null;
267 }
268
269 return arpEntry.getMacAddress();
270 }
271
272 private synchronized void updateArpTable(ARP arp){
273 InetAddress addr;
274 try {
275 addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
276 } catch (UnknownHostException e) {
277 log.warn("Unable to create InetAddress", e);
278 return;
279 }
280
281 ArpTableEntry arpEntry = arpTable.get(addr);
282
283 if (arpEntry != null
284 && arpEntry.getMacAddress() == arp.getSenderHardwareAddress()){
285 arpEntry.setTimeLastSeen(System.currentTimeMillis());
286 }
287 else {
288 arpTable.put(addr,
289 new ArpTableEntry(arp.getSenderHardwareAddress(),
290 System.currentTimeMillis()));
291 }
292 }
293
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200294 private void sendArpRequestForAddress(InetAddress ipAddress) {
295 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
296 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
297 byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
298 (byte)0xff, (byte)0xff, (byte)0xff};
299
300 ARP arpRequest = new ARP();
301
302 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
303 .setProtocolType(ARP.PROTO_TYPE_IP)
304 .setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
305 .setProtocolAddressLength((byte)4) //can't find the constant anywhere
306 .setOpCode(ARP.OP_REQUEST)
307 .setSenderHardwareAddress(zeroMac)
308 .setSenderProtocolAddress(zeroIpv4)
309 .setTargetHardwareAddress(zeroMac)
310 .setTargetProtocolAddress(ipAddress.getAddress());
311
312 Ethernet eth = new Ethernet();
313 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
314 .setSourceMACAddress(broadcastMac)
315 .setEtherType(Ethernet.TYPE_ARP)
316 .setPayload(arpRequest);
317
318 broadcastArpRequestOutEdge(eth.serialize(), 0, OFPort.OFPP_NONE.getValue());
319 }
320
321 //private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
322 private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200323 for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
324 Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
325 Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
326
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200327 if (linkPorts == null){
328 //I think this means the switch isn't known to topology yet.
329 //Maybe it only just joined.
330 continue;
331 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200332
333 OFPacketOut po = new OFPacketOut();
334 po.setInPort(OFPort.OFPP_NONE)
335 .setBufferId(-1)
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200336 .setPacketData(arpRequest);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200337
338 List<OFAction> actions = new ArrayList<OFAction>();
339
340 for (short portNum : enabledPorts){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200341 if (linkPorts.contains(portNum) ||
342 (sw.getId() == inSwitch && portNum == inPort)){
343 //If this port isn't an edge port or is the ingress port
344 //for the ARP, don't broadcast out it
345 continue;
346 }
347
348 actions.add(new OFActionOutput(portNum));
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200349 log.debug("Broadcasting out {}/{}", HexString.toHexString(sw.getId()), portNum);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200350 }
351
352 po.setActions(actions);
353 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
354 po.setActionsLength(actionsLength);
355 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200356 + arpRequest.length);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200357
358 List<OFMessage> msgList = new ArrayList<OFMessage>();
359 msgList.add(po);
360
361 try {
362 sw.write(msgList, null);
363 sw.flush();
364 } catch (IOException e) {
365 log.error("Failure writing packet out to switch", e);
366 }
367 }
368 }
369
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200370 public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac) {
371 //private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200372 ARP arpReply = new ARP();
373 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
374 .setProtocolType(ARP.PROTO_TYPE_IP)
375 .setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
376 .setProtocolAddressLength((byte)4) //can't find the constant anywhere
377 .setOpCode(ARP.OP_REPLY)
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200378 //.setSenderHardwareAddress(macRequested)
379 .setSenderHardwareAddress(targetMac)
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200380 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
381 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
382 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
383
384 Ethernet eth = new Ethernet();
385 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200386 //.setSourceMACAddress(macRequested)
387 .setSourceMACAddress(targetMac)
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200388 .setEtherType(Ethernet.TYPE_ARP)
389 .setPayload(arpReply);
390
391 List<OFAction> actions = new ArrayList<OFAction>();
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200392 //actions.add(new OFActionOutput(pi.getInPort()));
393 actions.add(new OFActionOutput(port));
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200394
395 OFPacketOut po = new OFPacketOut();
396 po.setInPort(OFPort.OFPP_NONE)
397 .setBufferId(-1)
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200398 .setPacketData(eth.serialize())
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200399 .setActions(actions)
400 .setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
401 .setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH
402 + po.getPacketData().length);
403
404 List<OFMessage> msgList = new ArrayList<OFMessage>();
405 msgList.add(po);
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200406
407 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
408
409 if (sw == null) {
410 return;
411 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200412
413 try {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200414 log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), port);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200415 sw.write(msgList, null);
416 sw.flush();
417 } catch (IOException e) {
418 log.warn("Failure writing packet out to switch", e);
419 }
420 }
Jonathan Hartc824ad02013-07-03 15:58:45 +1200421
422 //TODO this should be put somewhere more central. I use it in BgpRoute as well.
423 //We need a HexString.toHexString() equivalent.
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200424 private String bytesToStringAddr(byte[] bytes) {
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200425 InetAddress addr;
426 try {
427 addr = InetAddress.getByAddress(bytes);
428 } catch (UnknownHostException e) {
Jonathan Hartc824ad02013-07-03 15:58:45 +1200429 log.warn(" ", e);
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200430 return "";
431 }
432 if (addr == null) return "";
433 else return addr.getHostAddress();
434 }
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200435
436
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200437 public byte[] getMacAddress(InetAddress ipAddress) {
438 return lookupArpTable(ipAddress.getAddress());
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200439 }
Jonathan Hartf0c0dcb2013-07-24 15:28:42 +1200440
441 public byte[] sendArpRequest(InetAddress ipAddress, IArpRequester requester) {
442 byte[] lookupMac;
443 if ((lookupMac = lookupArpTable(ipAddress.getAddress())) == null) {
444 return lookupMac;
445 }
446
447 sendArpRequestForAddress(ipAddress);
448
449 storeRequester(ipAddress, requester);
450
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200451 return null;
452 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200453}