blob: 90c7db0ad9d4672d5dd3b6fc21b392a5778bbfba [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;
9import java.util.List;
10import java.util.Map;
11import java.util.Set;
12
13import net.floodlightcontroller.core.FloodlightContext;
14import net.floodlightcontroller.core.IFloodlightProviderService;
15import net.floodlightcontroller.core.IOFMessageListener;
16import net.floodlightcontroller.core.IOFSwitch;
17import net.floodlightcontroller.packet.ARP;
18import net.floodlightcontroller.packet.Ethernet;
19import net.floodlightcontroller.topology.ITopologyService;
20import net.floodlightcontroller.topology.NodePortTuple;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120021import net.floodlightcontroller.util.MACAddress;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120022
23import org.openflow.protocol.OFMessage;
24import org.openflow.protocol.OFPacketIn;
25import org.openflow.protocol.OFPacketOut;
26import org.openflow.protocol.OFPort;
27import org.openflow.protocol.OFType;
28import org.openflow.protocol.action.OFAction;
29import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120030import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120031import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
34public class ProxyArpManager implements IOFMessageListener {
35 private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
36
37 private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 5 mins)
38
39 protected IFloodlightProviderService floodlightProvider;
40 protected ITopologyService topology;
41
42
43 protected Map<InetAddress, ArpTableEntry> arpTable;
44
45 public ProxyArpManager(IFloodlightProviderService floodlightProvider,
46 ITopologyService topology){
47 this.floodlightProvider = floodlightProvider;
48 this.topology = topology;
49
50 arpTable = new HashMap<InetAddress, ArpTableEntry>();
51 }
52
53 @Override
54 public String getName() {
55 return "ProxyArpManager";
56 }
57
58 @Override
59 public boolean isCallbackOrderingPrereq(OFType type, String name) {
60 // TODO Auto-generated method stub
61 return false;
62 }
63
64 @Override
65 public boolean isCallbackOrderingPostreq(OFType type, String name) {
66 // TODO Auto-generated method stub
67 return false;
68 }
69
70 @Override
71 public Command receive(
72 IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
73
74 if (msg.getType() != OFType.PACKET_IN){
75 return Command.CONTINUE;
76 }
77
78 OFPacketIn pi = (OFPacketIn) msg;
79
80 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
81 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
82
83 if (eth.getEtherType() == Ethernet.TYPE_ARP){
84
85
86 ARP arp = (ARP) eth.getPayload();
87
88 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart8ec133c2013-06-26 15:25:18 +120089 log.debug("ARP request received for {}", bytesToStringAddr(arp.getTargetProtocolAddress()));
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120090
91 byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
92
93 if (mac == null){
94 //Mac address is not in our arp table.
95 //We need to flood the request out edge ports
96 Set<NodePortTuple> broadcastPorts = topology.getBroadcastDomainPorts();
97 log.debug("size {}", broadcastPorts.size());
98 for (NodePortTuple nodePort : broadcastPorts){
99 log.debug("Port {}", nodePort);
100 }
101 broadcastArpRequestOutEdge(pi, sw.getId(), pi.getInPort());
102 }
103 else {
104 //We know the address, so send a reply
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200105 log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200106 sendArpReply(arp, pi, mac, sw);
107 }
108 }
109 else if (arp.getOpCode() == ARP.OP_REPLY) {
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200110 log.debug("ARP reply recieved for {}", bytesToStringAddr(arp.getSenderProtocolAddress()));
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200111
112 log.debug("arp table {}", arpTable.keySet());
113
114 updateArpTable(arp);
115 }
116 }
117
118
119 return Command.CONTINUE;
120 }
121
122 private synchronized byte[] lookupArpTable(byte[] ipAddress){
123 InetAddress addr;
124 try {
125 addr = InetAddress.getByAddress(ipAddress);
126 } catch (UnknownHostException e) {
127 log.warn("Unable to create InetAddress", e);
128 return null;
129 }
130
131 ArpTableEntry arpEntry = arpTable.get(addr);
132
133 if (arpEntry == null){
134 return null;
135 }
136
137 if (System.currentTimeMillis() - arpEntry.getTimeLastSeen()
138 > ARP_ENTRY_TIMEOUT){
139 //Entry has timed out so we'll remove it and return null
140 arpTable.remove(addr);
141 return null;
142 }
143
144 return arpEntry.getMacAddress();
145 }
146
147 private synchronized void updateArpTable(ARP arp){
148 InetAddress addr;
149 try {
150 addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
151 } catch (UnknownHostException e) {
152 log.warn("Unable to create InetAddress", e);
153 return;
154 }
155
156 ArpTableEntry arpEntry = arpTable.get(addr);
157
158 if (arpEntry != null
159 && arpEntry.getMacAddress() == arp.getSenderHardwareAddress()){
160 arpEntry.setTimeLastSeen(System.currentTimeMillis());
161 }
162 else {
163 arpTable.put(addr,
164 new ArpTableEntry(arp.getSenderHardwareAddress(),
165 System.currentTimeMillis()));
166 }
167 }
168
169 private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
170 for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
171 Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
172 Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
173
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200174 if (linkPorts == null){
175 //I think this means the switch isn't known to topology yet.
176 //Maybe it only just joined.
177 continue;
178 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200179
180 OFPacketOut po = new OFPacketOut();
181 po.setInPort(OFPort.OFPP_NONE)
182 .setBufferId(-1)
183 //.setLengthU(OFActionOutput.MINIMUM_LENGTH);
184 .setPacketData(pi.getPacketData());
185
186 List<OFAction> actions = new ArrayList<OFAction>();
187
188 for (short portNum : enabledPorts){
189 log.debug("linkPorts {}", linkPorts);
190 log.debug("portNum {}", portNum);
191 if (linkPorts.contains(portNum) ||
192 (sw.getId() == inSwitch && portNum == inPort)){
193 //If this port isn't an edge port or is the ingress port
194 //for the ARP, don't broadcast out it
195 continue;
196 }
197
198 actions.add(new OFActionOutput(portNum));
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200199 log.debug("Broadcasting out {}/{}", HexString.toHexString(sw.getId()), portNum);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200200 }
201
202 po.setActions(actions);
203 short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
204 po.setActionsLength(actionsLength);
205 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
206 + pi.getPacketData().length);
207
208 List<OFMessage> msgList = new ArrayList<OFMessage>();
209 msgList.add(po);
210
211 try {
212 sw.write(msgList, null);
213 sw.flush();
214 } catch (IOException e) {
215 log.error("Failure writing packet out to switch", e);
216 }
217 }
218 }
219
220 private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
221 ARP arpReply = new ARP();
222 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
223 .setProtocolType(ARP.PROTO_TYPE_IP)
224 .setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
225 .setProtocolAddressLength((byte)4) //can't find the constant anywhere
226 .setOpCode(ARP.OP_REPLY)
227 .setSenderHardwareAddress(macRequested)
228 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
229 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
230 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
231
232 Ethernet eth = new Ethernet();
233 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
234 .setSourceMACAddress(macRequested)
235 .setEtherType(Ethernet.TYPE_ARP)
236 .setPayload(arpReply);
237
238 List<OFAction> actions = new ArrayList<OFAction>();
239 actions.add(new OFActionOutput(pi.getInPort()));
240
241 OFPacketOut po = new OFPacketOut();
242 po.setInPort(OFPort.OFPP_NONE)
243 .setBufferId(-1)
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200244 .setPacketData(eth.serialize())
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200245 .setActions(actions)
246 .setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
247 .setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH
248 + po.getPacketData().length);
249
250 List<OFMessage> msgList = new ArrayList<OFMessage>();
251 msgList.add(po);
252
253 try {
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200254 log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), pi.getInPort());
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200255 sw.write(msgList, null);
256 sw.flush();
257 } catch (IOException e) {
258 log.warn("Failure writing packet out to switch", e);
259 }
260 }
Jonathan Hart8ec133c2013-06-26 15:25:18 +1200261
262 private String bytesToStringAddr(byte[] bytes){
263 InetAddress addr;
264 try {
265 addr = InetAddress.getByAddress(bytes);
266 } catch (UnknownHostException e) {
267 // TODO Auto-generated catch block
268 e.printStackTrace();
269 return "";
270 }
271 if (addr == null) return "";
272 else return addr.getHostAddress();
273 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200274}