blob: 538bb41ad0e2f6d645b252397c4f360417ea8722 [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
117 @Override
118 public String getName() {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200119 return "ProxyArpManager";
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200120 }
121
122 @Override
123 public boolean isCallbackOrderingPrereq(OFType type, String name) {
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200124 return false;
125 }
126
127 @Override
128 public boolean isCallbackOrderingPostreq(OFType type, String name) {
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200129 return false;
130 }
131
132 @Override
133 public Command receive(
134 IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
135
136 if (msg.getType() != OFType.PACKET_IN){
137 return Command.CONTINUE;
138 }
139
140 OFPacketIn pi = (OFPacketIn) msg;
141
142 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
143 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
144
145 if (eth.getEtherType() == Ethernet.TYPE_ARP){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200146 ARP arp = (ARP) eth.getPayload();
147
148 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200149 handleArpRequest(sw, pi, arp);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200150 }
151 else if (arp.getOpCode() == ARP.OP_REPLY) {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200152 handleArpReply(sw, pi, arp);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200153 }
154 }
155
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200156 //TODO should we propagate ARP or swallow it?
157 //Always propagate for now so DeviceManager can learn the host location
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200158 return Command.CONTINUE;
159 }
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200160
161 protected void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
162 log.debug("ARP request received for {}",
163 bytesToStringAddr(arp.getTargetProtocolAddress()));
164
165 byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
166
167 if (mac == null){
168 //Mac address is not in our arp table.
169
170 //TODO check what the DeviceManager thinks
171
172 //Record where the request came from so we know where to send the reply
173 InetAddress target;
174 try {
175 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
176 } catch (UnknownHostException e) {
177 log.debug("Invalid address in ARP request", e);
178 //return Command.CONTINUE; //Continue or stop?
179 return;
180 }
181
182 synchronized (arpRequests) {
183 //arpRequests.putIfAbsent(target,
184 //Collections.synchronizedSet(new HashSet<ArpRequest>()));
185 // new ArpRequest());
186 //Set<ArpRequest> requesters = arpRequests.get(target);
187 if (arpRequests.get(target) == null) {
188 arpRequests.put(target, new ArpRequest());
189 }
190 ArpRequest request = arpRequests.get(target);
191
192 request.addRequester(new HostArpRequester(this, arp,
193 sw.getId(), pi.getInPort()));
194 }
195
196
197 //Flood the request out edge ports
198 broadcastArpRequestOutEdge(pi, sw.getId(), pi.getInPort());
199 }
200 else {
201 //We know the address, so send a reply
202 log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
203 //sendArpReply(arp, pi, mac, sw);
204 sendArpReply(arp, sw.getId(), pi.getInPort(), mac);
205 }
206 }
207
208 protected void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
209 log.debug("ARP reply recieved for {}",
210 bytesToStringAddr(arp.getSenderProtocolAddress()));
211
212 updateArpTable(arp);
213
214 //See if anyone's waiting for this ARP reply
215 InetAddress addr;
216 try {
217 addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
218 } catch (UnknownHostException e) {
219 return;
220 }
221
222 ArpRequest request = null;
223 synchronized (arpRequests) {
224 request = arpRequests.get(addr);
225 if (request != null) {
226 arpRequests.remove(addr);
227 }
228 }
229 if (request != null && !request.isExpired()) {
230 request.dispatchReply(arp.getSenderHardwareAddress());
231 }
232
233 /*
234 Set<ArpRequest> requests = arpRequests.get(addr);
235 if (requests != null){
236
237 synchronized (requests) {
238 for (ArpRequest request : requests) {
239 if (!request.isExpired()){
240 request.getRequester().arpResponse(
241 arp.getSenderHardwareAddress());
242 }
243 }
244 }
245 }*/
246 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200247
248 private synchronized byte[] lookupArpTable(byte[] ipAddress){
249 InetAddress addr;
250 try {
251 addr = InetAddress.getByAddress(ipAddress);
252 } catch (UnknownHostException e) {
253 log.warn("Unable to create InetAddress", e);
254 return null;
255 }
256
257 ArpTableEntry arpEntry = arpTable.get(addr);
258
259 if (arpEntry == null){
260 return null;
261 }
262
263 if (System.currentTimeMillis() - arpEntry.getTimeLastSeen()
264 > ARP_ENTRY_TIMEOUT){
265 //Entry has timed out so we'll remove it and return null
266 arpTable.remove(addr);
267 return null;
268 }
269
270 return arpEntry.getMacAddress();
271 }
272
273 private synchronized void updateArpTable(ARP arp){
274 InetAddress addr;
275 try {
276 addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
277 } catch (UnknownHostException e) {
278 log.warn("Unable to create InetAddress", e);
279 return;
280 }
281
282 ArpTableEntry arpEntry = arpTable.get(addr);
283
284 if (arpEntry != null
285 && arpEntry.getMacAddress() == arp.getSenderHardwareAddress()){
286 arpEntry.setTimeLastSeen(System.currentTimeMillis());
287 }
288 else {
289 arpTable.put(addr,
290 new ArpTableEntry(arp.getSenderHardwareAddress(),
291 System.currentTimeMillis()));
292 }
293 }
294
295 private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
296 for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
297 Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
298 Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
299
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200300 if (linkPorts == null){
301 //I think this means the switch isn't known to topology yet.
302 //Maybe it only just joined.
303 continue;
304 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200305
306 OFPacketOut po = new OFPacketOut();
307 po.setInPort(OFPort.OFPP_NONE)
308 .setBufferId(-1)
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200309 .setPacketData(pi.getPacketData());
310
311 List<OFAction> actions = new ArrayList<OFAction>();
312
313 for (short portNum : enabledPorts){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200314 if (linkPorts.contains(portNum) ||
315 (sw.getId() == inSwitch && portNum == inPort)){
316 //If this port isn't an edge port or is the ingress port
317 //for the ARP, don't broadcast out it
318 continue;
319 }
320
321 actions.add(new OFActionOutput(portNum));
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200322 log.debug("Broadcasting out {}/{}", HexString.toHexString(sw.getId()), portNum);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200323 }
324
325 po.setActions(actions);
326 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
327 po.setActionsLength(actionsLength);
328 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
329 + pi.getPacketData().length);
330
331 List<OFMessage> msgList = new ArrayList<OFMessage>();
332 msgList.add(po);
333
334 try {
335 sw.write(msgList, null);
336 sw.flush();
337 } catch (IOException e) {
338 log.error("Failure writing packet out to switch", e);
339 }
340 }
341 }
342
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200343 public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac) {
344 //private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200345 ARP arpReply = new ARP();
346 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
347 .setProtocolType(ARP.PROTO_TYPE_IP)
348 .setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
349 .setProtocolAddressLength((byte)4) //can't find the constant anywhere
350 .setOpCode(ARP.OP_REPLY)
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200351 //.setSenderHardwareAddress(macRequested)
352 .setSenderHardwareAddress(targetMac)
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200353 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
354 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
355 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
356
357 Ethernet eth = new Ethernet();
358 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200359 //.setSourceMACAddress(macRequested)
360 .setSourceMACAddress(targetMac)
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200361 .setEtherType(Ethernet.TYPE_ARP)
362 .setPayload(arpReply);
363
364 List<OFAction> actions = new ArrayList<OFAction>();
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200365 //actions.add(new OFActionOutput(pi.getInPort()));
366 actions.add(new OFActionOutput(port));
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200367
368 OFPacketOut po = new OFPacketOut();
369 po.setInPort(OFPort.OFPP_NONE)
370 .setBufferId(-1)
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200371 .setPacketData(eth.serialize())
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200372 .setActions(actions)
373 .setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
374 .setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH
375 + po.getPacketData().length);
376
377 List<OFMessage> msgList = new ArrayList<OFMessage>();
378 msgList.add(po);
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200379
380 IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
381
382 if (sw == null) {
383 return;
384 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200385
386 try {
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200387 log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), port);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200388 sw.write(msgList, null);
389 sw.flush();
390 } catch (IOException e) {
391 log.warn("Failure writing packet out to switch", e);
392 }
393 }
Jonathan Hartc824ad02013-07-03 15:58:45 +1200394
395 //TODO this should be put somewhere more central. I use it in BgpRoute as well.
396 //We need a HexString.toHexString() equivalent.
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200397 private String bytesToStringAddr(byte[] bytes){
398 InetAddress addr;
399 try {
400 addr = InetAddress.getByAddress(bytes);
401 } catch (UnknownHostException e) {
Jonathan Hartc824ad02013-07-03 15:58:45 +1200402 log.warn(" ", e);
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200403 return "";
404 }
405 if (addr == null) return "";
406 else return addr.getHostAddress();
407 }
Jonathan Hart6261dcd2013-07-22 17:58:35 +1200408
409
410 public byte[] lookupMac(InetAddress ipAddress){
411 //TODO implement
412 return null;
413 }
414 public byte[] sendArpRequest(InetAddress ipAddress, IArpRequester requester){
415 //TODO implement
416 return null;
417 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200418}