blob: 753529dc6981c244b6fe7cc5b51c542fe71d1602 [file] [log] [blame]
Jonathan Hart0961fe82014-04-03 09:56:25 -07001package net.onrc.onos.apps.proxyarp;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12002
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12003import java.net.InetAddress;
4import java.net.UnknownHostException;
TeruU7feef8a2014-04-03 00:15:49 -07005import java.nio.ByteBuffer;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +12006import java.util.ArrayList;
7import java.util.Collection;
Jonathan Harte93aed42013-12-05 18:39:50 -08008import java.util.HashMap;
Jonathan Hart6261dcd2013-07-22 17:58:35 +12009import java.util.Iterator;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120010import java.util.List;
11import java.util.Map;
12import java.util.Set;
Jonathan Hart6261dcd2013-07-22 17:58:35 +120013import java.util.Timer;
14import java.util.TimerTask;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120015
Jonathan Harte93aed42013-12-05 18:39:50 -080016import net.floodlightcontroller.core.module.FloodlightModuleContext;
17import net.floodlightcontroller.core.module.IFloodlightModule;
18import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart5afde492013-10-01 12:30:53 +130019import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120020import net.floodlightcontroller.util.MACAddress;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070021import net.onrc.onos.api.packet.IPacketListener;
22import net.onrc.onos.api.packet.IPacketService;
Yuta HIGUCHI238fa2a2014-05-01 09:56:46 -070023import net.onrc.onos.apps.proxyarp.web.ArpWebRoutable;
Jonathan Hart8f6dc092014-04-18 15:56:43 -070024import net.onrc.onos.apps.sdnip.Interface;
Jonathan Hart6df90172014-04-03 10:13:11 -070025import net.onrc.onos.core.datagrid.IDatagridService;
26import net.onrc.onos.core.datagrid.IEventChannel;
27import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070028import net.onrc.onos.core.main.config.IConfigInfoService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070029import net.onrc.onos.core.packet.ARP;
30import net.onrc.onos.core.packet.Ethernet;
31import net.onrc.onos.core.packet.IPv4;
Jonathan Hart472062d2014-04-03 10:56:48 -070032import net.onrc.onos.core.topology.Device;
Jonathan Harte37e4e22014-05-13 19:12:02 -070033import net.onrc.onos.core.topology.ITopologyService;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070034import net.onrc.onos.core.topology.Port;
Jonathan Hart472062d2014-04-03 10:56:48 -070035import net.onrc.onos.core.topology.Switch;
Jonathan Hart55316582014-05-09 10:02:58 -070036import net.onrc.onos.core.topology.Topology;
Jonathan Hart23701d12014-04-03 10:45:48 -070037import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120038
Jonathan Hart8ec133c2013-06-26 15:25:18 +120039import org.openflow.util.HexString;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Jonathan Hart4dfc3652013-08-02 20:22:36 +120043import com.google.common.collect.HashMultimap;
44import com.google.common.collect.Multimaps;
45import com.google.common.collect.SetMultimap;
Jonathan Hart55316582014-05-09 10:02:58 -070046import com.google.common.net.InetAddresses;
Jonathan Hart4dfc3652013-08-02 20:22:36 +120047
Jonathan Hart51dc5e12014-04-22 11:03:59 -070048public class ProxyArpManager implements IProxyArpService, IFloodlightModule,
49 IPacketListener {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070050 private static final Logger log = LoggerFactory
51 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080052
Jonathan Hart55316582014-05-09 10:02:58 -070053 private static long arpTimerPeriodConfig = 2000; // ms
TeruU3c049c42014-04-15 10:13:25 -070054 private static int arpRequestTimeoutConfig = 2000; // ms
55 private long arpCleaningTimerPeriodConfig = 60 * 1000; // ms (1 min)
Jonathan Hartda4d0e12013-09-30 21:00:20 +130056
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070057 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070058 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
TeruU3c049c42014-04-15 10:13:25 -070059 private IEventChannel<String, ArpCacheNotification> arpCacheEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070060 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU3c049c42014-04-15 10:13:25 -070061 private static final String ARP_CACHE_CHANNEL_NAME = "onos.arp_cache";
62 private final ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
TeruU3c049c42014-04-15 10:13:25 -070063 private final ArpCacheEventHandler arpCacheEventHandler = new ArpCacheEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070064
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070065 private IConfigInfoService configService;
66 private IRestApiService restApi;
Ray Milkey269ffb92014-04-03 14:43:30 -070067
Jonathan Harte37e4e22014-05-13 19:12:02 -070068 private ITopologyService topologyService;
69 private Topology topology;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070070 private IPacketService packetService;
Jonathan Harte93aed42013-12-05 18:39:50 -080071
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070072 private short vlan;
73 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080074
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070075 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120076
TeruU3c049c42014-04-15 10:13:25 -070077 private ArpCache arpCache;
78
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070079 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070080 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070081
Ray Milkey269ffb92014-04-03 14:43:30 -070082 @Override
83 public void entryAdded(ArpReplyNotification arpReply) {
84 log.debug("Received ARP reply notification for ip {}, mac {}",
85 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070086
Jonathan Hart55316582014-05-09 10:02:58 -070087 InetAddress addr = InetAddresses.fromInteger(arpReply.getTargetAddress());
88
89 sendArpReplyToWaitingRequesters(addr,
90 arpReply.getTargetMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -070091 }
92
93 @Override
94 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -070095 entryAdded(arpReply);
96 }
97
98 @Override
99 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -0700100 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
101 }
102 }
103
104 private class ArpCacheEventHandler implements
105 IEventChannelListener<String, ArpCacheNotification> {
106
107 /**
108 * Startup processing.
109 */
110 private void startUp() {
111 //
112 // TODO: Read all state from the database:
113 // For now, as a shortcut we read it from the datagrid
114 //
115 Collection<ArpCacheNotification> arpCacheEvents =
116 arpCacheEventChannel.getAllEntries();
117
118 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
119 entryAdded(arpCacheEvent);
120 }
121 }
122
123 @Override
124 public void entryAdded(ArpCacheNotification value) {
125
126 try {
127 log.debug("Received entryAdded for ARP cache notification for ip {}, mac {}",
128 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
129 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
130 } catch (UnknownHostException e) {
131 log.error("Exception : ", e);
132 }
133 }
134
135 @Override
136 public void entryRemoved(ArpCacheNotification value) {
137 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
138 value.getTargetAddress(), value.getTargetMacAddress());
139 try {
140 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
141 } catch (UnknownHostException e) {
142 log.error("Exception : ", e);
143 }
144 }
145
146 @Override
147 public void entryUpdated(ArpCacheNotification value) {
148 try {
149 log.debug("Received entryUpdated for ARP cache notification for ip {}, mac {}",
150 InetAddress.getByAddress(value.getTargetAddress()), value.getTargetMacAddress());
151 arpCache.update(InetAddress.getByAddress(value.getTargetAddress()), MACAddress.valueOf(value.getTargetMacAddress()));
152 } catch (UnknownHostException e) {
153 log.error("Exception : ", e);
154 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700156 }
157
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700158 private static class ArpRequest {
159 private final IArpRequester requester;
160 private final boolean retry;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700161 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200162
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700163 public ArpRequest(IArpRequester requester, boolean retry) {
164 this.requester = requester;
165 this.retry = retry;
Jonathan Hart55316582014-05-09 10:02:58 -0700166
167 requestTime = System.currentTimeMillis();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700168 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200169
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700170 public ArpRequest(ArpRequest old) {
171 this.requester = old.requester;
172 this.retry = old.retry;
173 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200174
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700175 public boolean isExpired() {
Jonathan Hart55316582014-05-09 10:02:58 -0700176 return ((System.currentTimeMillis() - requestTime)
177 > arpRequestTimeoutConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700178 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200179
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700180 public boolean shouldRetry() {
181 return retry;
182 }
183
184 public void dispatchReply(InetAddress ipAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700185 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700186 requester.arpResponse(ipAddress, replyMacAddress);
187 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700188 }
189
190 private class HostArpRequester implements IArpRequester {
191 private final ARP arpRequest;
192 private final long dpid;
193 private final short port;
194
195 public HostArpRequester(ARP arpRequest, long dpid, short port) {
196 this.arpRequest = arpRequest;
197 this.dpid = dpid;
198 this.port = port;
199 }
200
201 @Override
202 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
203 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
204 macAddress);
205 }
206 }
207
208 @Override
209 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
210 Collection<Class<? extends IFloodlightService>> l =
211 new ArrayList<Class<? extends IFloodlightService>>();
212 l.add(IProxyArpService.class);
213 return l;
214 }
215
216 @Override
217 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
218 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
219 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
220 m.put(IProxyArpService.class, this);
221 return m;
222 }
223
224 @Override
225 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
226 Collection<Class<? extends IFloodlightService>> dependencies =
227 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700228 dependencies.add(IRestApiService.class);
229 dependencies.add(IDatagridService.class);
230 dependencies.add(IConfigInfoService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700231 dependencies.add(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700232 dependencies.add(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700233 return dependencies;
234 }
235
236 @Override
237 public void init(FloodlightModuleContext context) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700238 this.configService = context.getServiceImpl(IConfigInfoService.class);
239 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700240 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700241 this.topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700242 this.packetService = context.getServiceImpl(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700243
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700244 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
245 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700246 }
247
248 @Override
249 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700250 Map<String, String> configOptions = context.getConfigParams(this);
251
TeruU8b2d1672014-04-25 17:02:56 -0700252 try {
253 arpCleaningTimerPeriodConfig = Long.parseLong(configOptions.get("cleanupmsec"));
254 } catch (NumberFormatException e) {
255 log.debug("ArpCleaningTimerPeriod related config options were not set. Use default.");
256 }
257
Jonathan Hart4aa47532014-04-23 15:23:41 -0700258 Long agingmsec = null;
259 try {
260 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
261 } catch (NumberFormatException e) {
262 log.debug("ArpEntryTimeout related config options were not set. Use default.");
263 }
264
265 arpCache = new ArpCache();
266 if (agingmsec != null) {
267 arpCache.setArpEntryTimeoutConfig(agingmsec);
268 }
269
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700270 this.vlan = configService.getVlan();
271 log.info("vlan set to {}", this.vlan);
272
273 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700274 packetService.registerPacketListener(this);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700275 topology = topologyService.getTopology();
Ray Milkey269ffb92014-04-03 14:43:30 -0700276
277 //
278 // Event notification setup: channels and event handlers
279 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700280
281 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
282 arpReplyEventHandler,
283 Long.class,
284 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700285
TeruU3c049c42014-04-15 10:13:25 -0700286 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
287 arpCacheEventHandler,
288 String.class,
289 ArpCacheNotification.class);
290 arpCacheEventHandler.startUp();
291
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700292 Timer arpTimer = new Timer("arp-processing");
293 arpTimer.scheduleAtFixedRate(new TimerTask() {
294 @Override
295 public void run() {
296 doPeriodicArpProcessing();
297 }
TeruU3c049c42014-04-15 10:13:25 -0700298 }, 0, arpTimerPeriodConfig);
299
300 Timer arpCacheTimer = new Timer("arp-clearning");
301 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
302 @Override
303 public void run() {
304 doPeriodicArpCleaning();
305 }
306 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700307 }
308
309 /*
310 * Function that runs periodically to manage the asynchronous request mechanism.
311 * It basically cleans up old ARP requests if we don't get a response for them.
312 * The caller can designate that a request should be retried indefinitely, and
313 * this task will handle that as well.
314 */
315 private void doPeriodicArpProcessing() {
316 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
317 .<InetAddress, ArpRequest>create();
318
Jonathan Hart55316582014-05-09 10:02:58 -0700319 // We must synchronize externally on the Multimap while using an
320 // iterator, even though it's a synchronizedMultimap
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700321 synchronized (arpRequests) {
322 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
323 .entries().iterator();
324
325 while (it.hasNext()) {
326 Map.Entry<InetAddress, ArpRequest> entry = it.next();
327 ArpRequest request = entry.getValue();
328 if (request.isExpired()) {
329 log.debug("Cleaning expired ARP request for {}", entry
330 .getKey().getHostAddress());
331
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700332 it.remove();
333
334 if (request.shouldRetry()) {
335 retryList.put(entry.getKey(), request);
336 }
337 }
338 }
339 }
340
341 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
342 .asMap().entrySet()) {
343
344 InetAddress address = entry.getKey();
345
346 log.debug("Resending ARP request for {}", address.getHostAddress());
347
348 // Only ARP requests sent by the controller will have the retry flag
349 // set, so for now we can just send a new ARP request for that
350 // address.
351 sendArpRequestForAddress(address);
352
353 for (ArpRequest request : entry.getValue()) {
354 arpRequests.put(address, new ArpRequest(request));
355 }
356 }
357 }
358
359 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700360 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruU8b2d1672014-04-25 17:02:56 -0700361
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700362 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
363 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700364 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700365 if (arp.getOpCode() == ARP.OP_REQUEST) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700366 handleArpRequest(sw.getDpid(), inPort.getNumber().shortValue(),
367 arp, eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700368 } else if (arp.getOpCode() == ARP.OP_REPLY) {
369 // For replies we simply send a notification via Hazelcast
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700370 sendArpReplyNotification(eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700371 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700372 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700373 }
pingping-linb8757bf2013-12-13 01:48:58 +0800374
TeruU3c049c42014-04-15 10:13:25 -0700375 private void learnArp(ARP arp) {
376 ArpCacheNotification arpCacheNotification = null;
377
378 arpCacheNotification = new ArpCacheNotification(arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
379
380 try {
381 arpCacheEventChannel.addEntry(InetAddress.getByAddress(arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
382 } catch (UnknownHostException e) {
383 log.error("Exception : ", e);
384 }
385 }
386
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700387 private void handleArpRequest(long dpid, short inPort, ARP arp, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700388 if (log.isTraceEnabled()) {
389 log.trace("ARP request received for {}",
390 inetAddressToString(arp.getTargetProtocolAddress()));
391 }
pingping-linb8757bf2013-12-13 01:48:58 +0800392
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700393 InetAddress target;
394 try {
395 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
396 } catch (UnknownHostException e) {
397 log.debug("Invalid address in ARP request", e);
398 return;
399 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700400
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700401 if (configService.fromExternalNetwork(dpid, inPort)) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700402 // If the request came from outside our network, we only care if
403 // it was a request for one of our interfaces.
404 if (configService.isInterfaceAddress(target)) {
405 log.trace(
406 "ARP request for our interface. Sending reply {} => {}",
407 target.getHostAddress(),
408 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800409
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700410 sendArpReply(arp, dpid, inPort,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700411 configService.getRouterMacAddress());
412 }
pingping-linb8757bf2013-12-13 01:48:58 +0800413
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700414 return;
415 }
pingping-linb8757bf2013-12-13 01:48:58 +0800416
TeruU3c049c42014-04-15 10:13:25 -0700417 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800418
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700419 arpRequests.put(target, new ArpRequest(
420 new HostArpRequester(arp, dpid, inPort), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800421
Jonathan Harte37e4e22014-05-13 19:12:02 -0700422 topology.acquireReadLock();
423 Device targetDevice = topology.getDeviceByMac(
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700424 MACAddress.valueOf(arp.getTargetHardwareAddress()));
Jonathan Harte37e4e22014-05-13 19:12:02 -0700425 topology.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800426
Ray Milkey269ffb92014-04-03 14:43:30 -0700427 if (targetDevice == null) {
428 if (log.isTraceEnabled()) {
429 log.trace("No device info found for {} - broadcasting",
430 target.getHostAddress());
431 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800432
Ray Milkey269ffb92014-04-03 14:43:30 -0700433 // We don't know the device so broadcast the request out
Jonathan Harte3702f22014-04-29 02:56:56 -0700434 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700435 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700436 } else {
437 // Even if the device exists in our database, we do not reply to
438 // the request directly, but check whether the device is still valid
439 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200440
Ray Milkey269ffb92014-04-03 14:43:30 -0700441 if (log.isTraceEnabled()) {
442 log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
443 new Object[]{
444 inetAddressToString(arp.getTargetProtocolAddress()),
445 macAddress,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700446 HexString.toHexString(dpid), inPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700447 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200448
Ray Milkey269ffb92014-04-03 14:43:30 -0700449 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800450
Ray Milkey269ffb92014-04-03 14:43:30 -0700451 Iterable<net.onrc.onos.core.topology.Port> outPorts = targetDevice.getAttachmentPoints();
452
453 if (!outPorts.iterator().hasNext()) {
454 if (log.isTraceEnabled()) {
455 log.trace("Device {} exists but is not connected to any ports" +
456 " - broadcasting", macAddress);
457 }
458
Jonathan Harte3702f22014-04-29 02:56:56 -0700459 packetService.broadcastPacketOutEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700460 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700461 } else {
462 for (net.onrc.onos.core.topology.Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700463
464 if (portObject.getOutgoingLink() != null || portObject.getIncomingLink() != null) {
465 continue;
466 }
467
468 short outPort = portObject.getNumber().shortValue();
469 Switch outSwitchObject = portObject.getSwitch();
470 long outSwitch = outSwitchObject.getDpid();
471
472 if (log.isTraceEnabled()) {
473 log.trace("Probing device {} on port {}/{}",
474 new Object[]{macAddress,
475 HexString.toHexString(outSwitch), outPort});
476 }
477
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700478 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700479 eth, new SwitchPort(outSwitch, outPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700480 }
481 }
482 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700483 }
pingping-linb8757bf2013-12-13 01:48:58 +0800484
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700485 // TODO this method has not been tested after recent implementation changes.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700486 private void sendArpRequestForAddress(InetAddress ipAddress) {
487 // TODO what should the sender IP address and MAC address be if no
488 // IP addresses are configured? Will there ever be a need to send
489 // ARP requests from the controller in that case?
490 // All-zero MAC address doesn't seem to work - hosts don't respond to it
491
492 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
493 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
494 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
495 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
496 (byte) 0xff, (byte) 0xff, (byte) 0xff};
497
498 ARP arpRequest = new ARP();
499
500 arpRequest
501 .setHardwareType(ARP.HW_TYPE_ETHERNET)
502 .setProtocolType(ARP.PROTO_TYPE_IP)
503 .setHardwareAddressLength(
504 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
505 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
506 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
507 .setTargetProtocolAddress(ipAddress.getAddress());
508
509 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700510 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700511 byte[] senderMacAddress = genericNonZeroMac;
512 if (routerMacAddress != null) {
513 senderMacAddress = routerMacAddress.toBytes();
514 }
515 arpRequest.setSenderHardwareAddress(senderMacAddress);
516
517 byte[] senderIPAddress = zeroIpv4;
518 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700519 if (intf == null) {
520 // TODO handle the case where the controller needs to send an ARP
521 // request but there's not IP configuration. In this case the
522 // request should be broadcast out all edge ports in the network.
523 log.warn("Sending ARP requests with default configuration "
524 + "not supported");
525 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700526 }
527
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700528 senderIPAddress = intf.getIpAddress().getAddress();
529
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700530 arpRequest.setSenderProtocolAddress(senderIPAddress);
531
532 Ethernet eth = new Ethernet();
533 eth.setSourceMACAddress(senderMacAddress)
534 .setDestinationMACAddress(broadcastMac)
535 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
536
537 if (vlan != NO_VLAN) {
538 eth.setVlanID(vlan).setPriorityCode((byte) 0);
539 }
540
541 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700542
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700543 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700544 eth, new SwitchPort(intf.getDpid(), intf.getPort()));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700545 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700546
TeruU3c049c42014-04-15 10:13:25 -0700547 //Please leave it for now because this code is needed for SDN-IP. It will be removed soon.
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700548 /*
TeruU7feef8a2014-04-03 00:15:49 -0700549 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700550 sendArpRequestToSwitches(dstAddress, arpRequest,
551 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700552 }
553
TeruU3c049c42014-04-15 10:13:25 -0700554 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
555 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700556
557 if (configService.hasLayer3Configuration()) {
558 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700559 if (intf != null) {
560 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
561 }
562 else {
563 //TODO here it should be broadcast out all non-interface edge ports.
564 //I think we can assume that if it's not a request for an external
565 //network, it's an ARP for a host in our own network. So we want to
566 //send it out all edge ports that don't have an interface configured
567 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700568 log.debug("No interface found to send ARP request for {}",
569 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700570 }
TeruU3c049c42014-04-15 10:13:25 -0700571 }
572 else {
573 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700574 }
575 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700576 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700577
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700578 private void sendArpReplyNotification(Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700579 ARP arp = (ARP) eth.getPayload();
580
581 if (log.isTraceEnabled()) {
582 log.trace("Sending ARP reply for {} to other ONOS instances",
583 inetAddressToString(arp.getSenderProtocolAddress()));
584 }
585
586 InetAddress targetAddress;
587
588 try {
589 targetAddress = InetAddress.getByAddress(arp
590 .getSenderProtocolAddress());
591 } catch (UnknownHostException e) {
592 log.error("Unknown host", e);
593 return;
594 }
595
596 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
597
TeruU3c049c42014-04-15 10:13:25 -0700598 ArpReplyNotification value =
Ray Milkey269ffb92014-04-03 14:43:30 -0700599 new ArpReplyNotification(ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
600 log.debug("ArpReplyNotification ip {}, mac{}", ByteBuffer.wrap(targetAddress.getAddress()).getInt(), mac);
TeruU3c049c42014-04-15 10:13:25 -0700601 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700602 }
603
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700604 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700605 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700606 if (log.isTraceEnabled()) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700607 log.trace("Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700608 new Object[]{
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700609 inetAddressToString(arpRequest.getTargetProtocolAddress()),
610 targetMac, inetAddressToString(arpRequest
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700611 .getSenderProtocolAddress())});
612 }
613
614 ARP arpReply = new ARP();
615 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
616 .setProtocolType(ARP.PROTO_TYPE_IP)
617 .setHardwareAddressLength(
618 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
619 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
620 .setOpCode(ARP.OP_REPLY)
621 .setSenderHardwareAddress(targetMac.toBytes())
622 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
623 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
624 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
625
626 Ethernet eth = new Ethernet();
627 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
628 .setSourceMACAddress(targetMac.toBytes())
629 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
630
631 if (vlan != NO_VLAN) {
632 eth.setVlanID(vlan).setPriorityCode((byte) 0);
633 }
634
Jonathan Harte3702f22014-04-29 02:56:56 -0700635 packetService.sendPacket(eth, new SwitchPort(dpid, port));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700636 }
637
638 private String inetAddressToString(byte[] bytes) {
639 try {
640 return InetAddress.getByAddress(bytes).getHostAddress();
641 } catch (UnknownHostException e) {
642 log.debug("Invalid IP address", e);
643 return "";
644 }
645 }
646
647 /*
648 * IProxyArpService methods
649 */
650
651 @Override
652 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700653 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700654 }
655
656 @Override
657 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700658 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700659 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
660
661 // Sanity check to make sure we don't send a request for our own address
662 if (!configService.isInterfaceAddress(ipAddress)) {
663 sendArpRequestForAddress(ipAddress);
664 }
665 }
666
667 @Override
668 public List<String> getMappings() {
TeruU8b2d1672014-04-25 17:02:56 -0700669 return arpCache.getMappings();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700670 }
671
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700672 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700673 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700674 log.debug("Sending ARP reply for {} to requesters",
675 address.getHostAddress());
676
677 // See if anyone's waiting for this ARP reply
678 Set<ArpRequest> requests = arpRequests.get(address);
679
680 // Synchronize on the Multimap while using an iterator for one of the
681 // sets
682 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
683 requests.size());
684 synchronized (arpRequests) {
685 Iterator<ArpRequest> it = requests.iterator();
686 while (it.hasNext()) {
687 ArpRequest request = it.next();
688 it.remove();
689 requestsToSend.add(request);
690 }
691 }
692
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700693 // Don't hold an ARP lock while dispatching requests
694 for (ArpRequest request : requestsToSend) {
695 request.dispatchReply(address, mac);
696 }
697 }
TeruU3c049c42014-04-15 10:13:25 -0700698
699 private void doPeriodicArpCleaning() {
700 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
701 for (InetAddress expireIp : expiredipslist) {
702 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
703 arpCacheEventChannel.removeEntry(expireIp.toString());
704 }
705 }
TeruU8b2d1672014-04-25 17:02:56 -0700706
707 public long getArpEntryTimeout() {
708 return arpCache.getArpEntryTimeout();
709 }
710
711 public long getArpCleaningTimerPeriod() {
712 return arpCleaningTimerPeriodConfig;
713 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700714}