blob: 197b40dd93c352a84ccbf0f9a1c8e1f0ccf2879f [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;
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
Jonathan Harte93aed42013-12-05 18:39:50 -080015import net.floodlightcontroller.core.module.FloodlightModuleContext;
16import net.floodlightcontroller.core.module.IFloodlightModule;
17import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart5afde492013-10-01 12:30:53 +130018import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart8ec133c2013-06-26 15:25:18 +120019import net.floodlightcontroller.util.MACAddress;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070020import net.onrc.onos.api.packet.IPacketListener;
21import net.onrc.onos.api.packet.IPacketService;
Yuta HIGUCHI238fa2a2014-05-01 09:56:46 -070022import net.onrc.onos.apps.proxyarp.web.ArpWebRoutable;
Jonathan Hart8f6dc092014-04-18 15:56:43 -070023import net.onrc.onos.apps.sdnip.Interface;
Jonathan Hart6df90172014-04-03 10:13:11 -070024import net.onrc.onos.core.datagrid.IDatagridService;
25import net.onrc.onos.core.datagrid.IEventChannel;
26import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070027import net.onrc.onos.core.main.config.IConfigInfoService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070028import net.onrc.onos.core.packet.ARP;
29import net.onrc.onos.core.packet.Ethernet;
30import net.onrc.onos.core.packet.IPv4;
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -070031import net.onrc.onos.core.topology.Host;
Jonathan Harte37e4e22014-05-13 19:12:02 -070032import net.onrc.onos.core.topology.ITopologyService;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070033import net.onrc.onos.core.topology.Port;
Jonathan Hart472062d2014-04-03 10:56:48 -070034import net.onrc.onos.core.topology.Switch;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070035import net.onrc.onos.core.topology.MutableTopology;
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -070036import net.onrc.onos.core.util.Dpid;
37import net.onrc.onos.core.util.PortNumber;
Jonathan Hart23701d12014-04-03 10:45:48 -070038import net.onrc.onos.core.util.SwitchPort;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120039
Jonathan Hartc78b8f62014-08-07 22:31:09 -070040import org.projectfloodlight.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;
Jonathan Hart55316582014-05-09 10:02:58 -070047import com.google.common.net.InetAddresses;
Jonathan Hart4dfc3652013-08-02 20:22:36 +120048
Jonathan Hart51dc5e12014-04-22 11:03:59 -070049public class ProxyArpManager implements IProxyArpService, IFloodlightModule,
50 IPacketListener {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070051 private static final Logger log = LoggerFactory
52 .getLogger(ProxyArpManager.class);
pingping-lin017a8922013-12-11 11:15:33 +080053
Jonathan Hart55316582014-05-09 10:02:58 -070054 private static long arpTimerPeriodConfig = 2000; // ms
TeruU3c049c42014-04-15 10:13:25 -070055 private static int arpRequestTimeoutConfig = 2000; // ms
56 private long arpCleaningTimerPeriodConfig = 60 * 1000; // ms (1 min)
Jonathan Hartda4d0e12013-09-30 21:00:20 +130057
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070058 private IDatagridService datagrid;
TeruU7feef8a2014-04-03 00:15:49 -070059 private IEventChannel<Long, ArpReplyNotification> arpReplyEventChannel;
TeruU3c049c42014-04-15 10:13:25 -070060 private IEventChannel<String, ArpCacheNotification> arpCacheEventChannel;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070061 private static final String ARP_REPLY_CHANNEL_NAME = "onos.arp_reply";
TeruU3c049c42014-04-15 10:13:25 -070062 private static final String ARP_CACHE_CHANNEL_NAME = "onos.arp_cache";
63 private final ArpReplyEventHandler arpReplyEventHandler = new ArpReplyEventHandler();
TeruU3c049c42014-04-15 10:13:25 -070064 private final ArpCacheEventHandler arpCacheEventHandler = new ArpCacheEventHandler();
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070065
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070066 private IConfigInfoService configService;
67 private IRestApiService restApi;
Ray Milkey269ffb92014-04-03 14:43:30 -070068
Jonathan Harte37e4e22014-05-13 19:12:02 -070069 private ITopologyService topologyService;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070070 private MutableTopology mutableTopology;
Jonathan Hart51dc5e12014-04-22 11:03:59 -070071 private IPacketService packetService;
Jonathan Harte93aed42013-12-05 18:39:50 -080072
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070073 private short vlan;
74 private static final short NO_VLAN = 0;
Jonathan Harte93aed42013-12-05 18:39:50 -080075
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070076 private SetMultimap<InetAddress, ArpRequest> arpRequests;
Jonathan Hartdf6ec332013-08-04 01:37:14 +120077
TeruU3c049c42014-04-15 10:13:25 -070078 private ArpCache arpCache;
79
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070080 private class ArpReplyEventHandler implements
Ray Milkey269ffb92014-04-03 14:43:30 -070081 IEventChannelListener<Long, ArpReplyNotification> {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070082
Ray Milkey269ffb92014-04-03 14:43:30 -070083 @Override
84 public void entryAdded(ArpReplyNotification arpReply) {
85 log.debug("Received ARP reply notification for ip {}, mac {}",
86 arpReply.getTargetAddress(), arpReply.getTargetMacAddress());
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070087
Jonathan Hart55316582014-05-09 10:02:58 -070088 InetAddress addr = InetAddresses.fromInteger(arpReply.getTargetAddress());
89
90 sendArpReplyToWaitingRequesters(addr,
91 arpReply.getTargetMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -070092 }
93
94 @Override
95 public void entryUpdated(ArpReplyNotification arpReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -070096 entryAdded(arpReply);
97 }
98
99 @Override
100 public void entryRemoved(ArpReplyNotification arpReply) {
TeruU3c049c42014-04-15 10:13:25 -0700101 //Not implemented. ArpReplyEventHandler is used only for remote messaging.
102 }
103 }
104
105 private class ArpCacheEventHandler implements
106 IEventChannelListener<String, ArpCacheNotification> {
107
108 /**
109 * Startup processing.
110 */
111 private void startUp() {
112 //
113 // TODO: Read all state from the database:
114 // For now, as a shortcut we read it from the datagrid
115 //
116 Collection<ArpCacheNotification> arpCacheEvents =
117 arpCacheEventChannel.getAllEntries();
118
119 for (ArpCacheNotification arpCacheEvent : arpCacheEvents) {
120 entryAdded(arpCacheEvent);
121 }
122 }
123
124 @Override
125 public void entryAdded(ArpCacheNotification value) {
TeruU3c049c42014-04-15 10:13:25 -0700126 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700127 InetAddress targetIpAddress =
128 InetAddress.getByAddress(value.getTargetAddress());
129
130 log.debug("Received entryAdded for ARP cache notification " +
131 "for ip {}, mac {}", targetIpAddress, value.getTargetMacAddress());
132 arpCache.update(targetIpAddress,
133 MACAddress.valueOf(value.getTargetMacAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700134 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700135 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700136 }
137 }
138
139 @Override
140 public void entryRemoved(ArpCacheNotification value) {
141 log.debug("Received entryRemoved for ARP cache notification for ip {}, mac {}",
142 value.getTargetAddress(), value.getTargetMacAddress());
143 try {
144 arpCache.remove(InetAddress.getByAddress(value.getTargetAddress()));
145 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700146 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700147 }
148 }
149
150 @Override
151 public void entryUpdated(ArpCacheNotification value) {
152 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700153 InetAddress targetIpAddress =
154 InetAddress.getByAddress(value.getTargetAddress());
155
156 log.debug("Received entryUpdated for ARP cache notification " +
157 "for ip {}, mac {}", targetIpAddress, value.getTargetMacAddress());
158 arpCache.update(targetIpAddress,
159 MACAddress.valueOf(value.getTargetMacAddress()));
TeruU3c049c42014-04-15 10:13:25 -0700160 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700161 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700162 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700163 }
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700164 }
165
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700166 private static class ArpRequest {
167 private final IArpRequester requester;
168 private final boolean retry;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700169 private long requestTime;
Jonathan Hartdf6ec332013-08-04 01:37:14 +1200170
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700171 public ArpRequest(IArpRequester requester, boolean retry) {
172 this.requester = requester;
173 this.retry = retry;
Jonathan Hart55316582014-05-09 10:02:58 -0700174
175 requestTime = System.currentTimeMillis();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700176 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200177
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700178 public ArpRequest(ArpRequest old) {
179 this.requester = old.requester;
180 this.retry = old.retry;
181 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200182
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700183 public boolean isExpired() {
Jonathan Hart55316582014-05-09 10:02:58 -0700184 return ((System.currentTimeMillis() - requestTime)
185 > arpRequestTimeoutConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700186 }
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200187
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700188 public boolean shouldRetry() {
189 return retry;
190 }
191
192 public void dispatchReply(InetAddress ipAddress,
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 MACAddress replyMacAddress) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700194 requester.arpResponse(ipAddress, replyMacAddress);
195 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700196 }
197
198 private class HostArpRequester implements IArpRequester {
199 private final ARP arpRequest;
200 private final long dpid;
201 private final short port;
202
203 public HostArpRequester(ARP arpRequest, long dpid, short port) {
204 this.arpRequest = arpRequest;
205 this.dpid = dpid;
206 this.port = port;
207 }
208
209 @Override
210 public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
211 ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
212 macAddress);
213 }
214 }
215
216 @Override
217 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
218 Collection<Class<? extends IFloodlightService>> l =
219 new ArrayList<Class<? extends IFloodlightService>>();
220 l.add(IProxyArpService.class);
221 return l;
222 }
223
224 @Override
225 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
226 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
227 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
228 m.put(IProxyArpService.class, this);
229 return m;
230 }
231
232 @Override
233 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
234 Collection<Class<? extends IFloodlightService>> dependencies =
235 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700236 dependencies.add(IRestApiService.class);
237 dependencies.add(IDatagridService.class);
238 dependencies.add(IConfigInfoService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700239 dependencies.add(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700240 dependencies.add(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700241 return dependencies;
242 }
243
244 @Override
245 public void init(FloodlightModuleContext context) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700246 this.configService = context.getServiceImpl(IConfigInfoService.class);
247 this.restApi = context.getServiceImpl(IRestApiService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700248 this.datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700249 this.topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700250 this.packetService = context.getServiceImpl(IPacketService.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700251
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700252 arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
253 .<InetAddress, ArpRequest>create());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700254 }
255
256 @Override
257 public void startUp(FloodlightModuleContext context) {
Jonathan Hart4aa47532014-04-23 15:23:41 -0700258 Map<String, String> configOptions = context.getConfigParams(this);
259
TeruU8b2d1672014-04-25 17:02:56 -0700260 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700261 arpCleaningTimerPeriodConfig =
262 Long.parseLong(configOptions.get("cleanupmsec"));
TeruU8b2d1672014-04-25 17:02:56 -0700263 } catch (NumberFormatException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700264 log.debug("ArpCleaningTimerPeriod related config options were " +
265 "not set. Using default.");
TeruU8b2d1672014-04-25 17:02:56 -0700266 }
267
Jonathan Hart4aa47532014-04-23 15:23:41 -0700268 Long agingmsec = null;
269 try {
270 agingmsec = Long.parseLong(configOptions.get("agingmsec"));
271 } catch (NumberFormatException e) {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700272 log.debug("ArpEntryTimeout related config options were " +
273 "not set. Using default.");
Jonathan Hart4aa47532014-04-23 15:23:41 -0700274 }
275
276 arpCache = new ArpCache();
277 if (agingmsec != null) {
278 arpCache.setArpEntryTimeoutConfig(agingmsec);
279 }
280
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700281 this.vlan = configService.getVlan();
282 log.info("vlan set to {}", this.vlan);
283
284 restApi.addRestletRoutable(new ArpWebRoutable());
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700285 packetService.registerPacketListener(this);
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700286 mutableTopology = topologyService.getTopology();
Ray Milkey269ffb92014-04-03 14:43:30 -0700287
288 //
289 // Event notification setup: channels and event handlers
290 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700291
292 arpReplyEventChannel = datagrid.addListener(ARP_REPLY_CHANNEL_NAME,
293 arpReplyEventHandler,
294 Long.class,
295 ArpReplyNotification.class);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700296
TeruU3c049c42014-04-15 10:13:25 -0700297 arpCacheEventChannel = datagrid.addListener(ARP_CACHE_CHANNEL_NAME,
298 arpCacheEventHandler,
299 String.class,
300 ArpCacheNotification.class);
301 arpCacheEventHandler.startUp();
302
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700303 Timer arpTimer = new Timer("arp-processing");
304 arpTimer.scheduleAtFixedRate(new TimerTask() {
305 @Override
306 public void run() {
307 doPeriodicArpProcessing();
308 }
TeruU3c049c42014-04-15 10:13:25 -0700309 }, 0, arpTimerPeriodConfig);
310
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700311 Timer arpCacheTimer = new Timer("arp-cleaning");
TeruU3c049c42014-04-15 10:13:25 -0700312 arpCacheTimer.scheduleAtFixedRate(new TimerTask() {
313 @Override
314 public void run() {
315 doPeriodicArpCleaning();
316 }
317 }, 0, arpCleaningTimerPeriodConfig);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700318 }
319
320 /*
321 * Function that runs periodically to manage the asynchronous request mechanism.
322 * It basically cleans up old ARP requests if we don't get a response for them.
323 * The caller can designate that a request should be retried indefinitely, and
324 * this task will handle that as well.
325 */
326 private void doPeriodicArpProcessing() {
327 SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
328 .<InetAddress, ArpRequest>create();
329
Jonathan Hart55316582014-05-09 10:02:58 -0700330 // We must synchronize externally on the Multimap while using an
331 // iterator, even though it's a synchronizedMultimap
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700332 synchronized (arpRequests) {
333 Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
334 .entries().iterator();
335
336 while (it.hasNext()) {
337 Map.Entry<InetAddress, ArpRequest> entry = it.next();
338 ArpRequest request = entry.getValue();
339 if (request.isExpired()) {
340 log.debug("Cleaning expired ARP request for {}", entry
341 .getKey().getHostAddress());
342
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700343 it.remove();
344
345 if (request.shouldRetry()) {
346 retryList.put(entry.getKey(), request);
347 }
348 }
349 }
350 }
351
352 for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
353 .asMap().entrySet()) {
354
355 InetAddress address = entry.getKey();
356
357 log.debug("Resending ARP request for {}", address.getHostAddress());
358
359 // Only ARP requests sent by the controller will have the retry flag
360 // set, so for now we can just send a new ARP request for that
361 // address.
362 sendArpRequestForAddress(address);
363
364 for (ArpRequest request : entry.getValue()) {
365 arpRequests.put(address, new ArpRequest(request));
366 }
367 }
368 }
369
370 @Override
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700371 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruU8b2d1672014-04-25 17:02:56 -0700372
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700373 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
374 ARP arp = (ARP) eth.getPayload();
TeruU3c049c42014-04-15 10:13:25 -0700375 learnArp(arp);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700376 if (arp.getOpCode() == ARP.OP_REQUEST) {
Yuta HIGUCHI9da3a6e2014-06-10 22:11:58 -0700377 handleArpRequest(sw.getDpid().value(), inPort.getNumber().shortValue(),
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700378 arp, eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700379 } else if (arp.getOpCode() == ARP.OP_REPLY) {
380 // For replies we simply send a notification via Hazelcast
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700381 sendArpReplyNotification(eth);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700382 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700383 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700384 }
pingping-linb8757bf2013-12-13 01:48:58 +0800385
TeruU3c049c42014-04-15 10:13:25 -0700386 private void learnArp(ARP arp) {
387 ArpCacheNotification arpCacheNotification = null;
388
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700389 arpCacheNotification = new ArpCacheNotification(
390 arp.getSenderProtocolAddress(), arp.getSenderHardwareAddress());
TeruU3c049c42014-04-15 10:13:25 -0700391
392 try {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700393 arpCacheEventChannel.addEntry(InetAddress.getByAddress(
394 arp.getSenderProtocolAddress()).toString(), arpCacheNotification);
TeruU3c049c42014-04-15 10:13:25 -0700395 } catch (UnknownHostException e) {
Naoki Shiota72afb332014-07-22 17:39:00 -0700396 log.error("Unknown host", e);
TeruU3c049c42014-04-15 10:13:25 -0700397 }
398 }
399
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700400 private void handleArpRequest(long dpid, short inPort, ARP arp, Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700401 if (log.isTraceEnabled()) {
402 log.trace("ARP request received for {}",
403 inetAddressToString(arp.getTargetProtocolAddress()));
404 }
pingping-linb8757bf2013-12-13 01:48:58 +0800405
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700406 InetAddress target;
pingping-lin0426dee2014-08-27 15:03:17 -0700407 InetAddress sender;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700408 try {
409 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
pingping-lin0426dee2014-08-27 15:03:17 -0700410 sender = InetAddress.getByAddress(arp.getSenderProtocolAddress());
411
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700412 } catch (UnknownHostException e) {
413 log.debug("Invalid address in ARP request", e);
414 return;
415 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700416
pingping-lin0426dee2014-08-27 15:03:17 -0700417 // Handle ARP from external network
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700418 if (configService.fromExternalNetwork(dpid, inPort)) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700419 // If the request came from outside our network, we only care if
420 // it was a request for one of our interfaces.
421 if (configService.isInterfaceAddress(target)) {
422 log.trace(
423 "ARP request for our interface. Sending reply {} => {}",
424 target.getHostAddress(),
425 configService.getRouterMacAddress());
pingping-linb8757bf2013-12-13 01:48:58 +0800426
pingping-lin0426dee2014-08-27 15:03:17 -0700427 //TODO: learn MAC address dynamically rather than from configuration
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700428 sendArpReply(arp, dpid, inPort,
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700429 configService.getRouterMacAddress());
430 }
pingping-linb8757bf2013-12-13 01:48:58 +0800431
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700432 return;
433 }
pingping-linb8757bf2013-12-13 01:48:58 +0800434
pingping-lin0426dee2014-08-27 15:03:17 -0700435 // Handle ARP to external network
436 if (configService.inConnectedNetwork(target)
437 && configService.isInterfaceAddress(sender)) {
438 SwitchPort switchPort =
439 configService.getOutgoingInterface(target).getSwitchPort();
440 arpRequests.put(target, new ArpRequest(
441 new HostArpRequester(arp, dpid, inPort), false));
442 packetService.sendPacket(eth, switchPort);
443 return;
444 }
445
TeruU3c049c42014-04-15 10:13:25 -0700446 //MACAddress mac = arpCache.lookup(target);
pingping-linb8757bf2013-12-13 01:48:58 +0800447
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700448 arpRequests.put(target, new ArpRequest(
449 new HostArpRequester(arp, dpid, inPort), false));
pingping-linb8757bf2013-12-13 01:48:58 +0800450
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700451 mutableTopology.acquireReadLock();
452 Host targetHost = mutableTopology.getHostByMac(
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700453 MACAddress.valueOf(arp.getTargetHardwareAddress()));
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700454 mutableTopology.releaseReadLock();
pingping-linb8757bf2013-12-13 01:48:58 +0800455
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700456 if (targetHost == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700457 if (log.isTraceEnabled()) {
458 log.trace("No device info found for {} - broadcasting",
459 target.getHostAddress());
460 }
Jonathan Hart18ad9502013-12-15 18:28:00 -0800461
Ray Milkey269ffb92014-04-03 14:43:30 -0700462 // We don't know the device so broadcast the request out
pingping-lin0426dee2014-08-27 15:03:17 -0700463 packetService.broadcastPacketOutInternalEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700464 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700465 } else {
466 // Even if the device exists in our database, we do not reply to
467 // the request directly, but check whether the device is still valid
468 MACAddress macAddress = MACAddress.valueOf(arp.getTargetHardwareAddress());
Jonathan Hart2f790d22013-08-15 14:01:24 +1200469
Ray Milkey269ffb92014-04-03 14:43:30 -0700470 if (log.isTraceEnabled()) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700471 log.trace("The target Host Record in DB is: {} => {} " +
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700472 "from ARP request host at {}/{}", new Object[]{
Ray Milkey269ffb92014-04-03 14:43:30 -0700473 inetAddressToString(arp.getTargetProtocolAddress()),
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700474 macAddress, HexString.toHexString(dpid), inPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700475 }
Jonathan Hart5b803bc2013-09-23 14:46:11 +1200476
Ray Milkey269ffb92014-04-03 14:43:30 -0700477 // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
Jonathan Hart7804bea2014-01-07 10:50:52 -0800478
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700479 Iterable<Port> outPorts = targetHost.getAttachmentPoints();
Ray Milkey269ffb92014-04-03 14:43:30 -0700480
481 if (!outPorts.iterator().hasNext()) {
482 if (log.isTraceEnabled()) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700483 log.trace("Host {} exists but is not connected to any ports" +
Ray Milkey269ffb92014-04-03 14:43:30 -0700484 " - broadcasting", macAddress);
485 }
486
pingping-lin0426dee2014-08-27 15:03:17 -0700487 packetService.broadcastPacketOutInternalEdge(eth,
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700488 new SwitchPort(dpid, inPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 } else {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700490 for (Port portObject : outPorts) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700491
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700492 if (portObject.getOutgoingLink() != null ||
493 portObject.getIncomingLink() != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700494 continue;
495 }
496
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700497 PortNumber outPort = portObject.getNumber();
Ray Milkey269ffb92014-04-03 14:43:30 -0700498 Switch outSwitchObject = portObject.getSwitch();
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700499 Dpid outSwitch = outSwitchObject.getDpid();
Ray Milkey269ffb92014-04-03 14:43:30 -0700500
501 if (log.isTraceEnabled()) {
502 log.trace("Probing device {} on port {}/{}",
503 new Object[]{macAddress,
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700504 outSwitch, outPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700505 }
506
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700507 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700508 eth, new SwitchPort(outSwitch, outPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700509 }
510 }
511 }
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700512 }
pingping-linb8757bf2013-12-13 01:48:58 +0800513
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700514 // TODO this method has not been tested after recent implementation changes.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700515 private void sendArpRequestForAddress(InetAddress ipAddress) {
516 // TODO what should the sender IP address and MAC address be if no
517 // IP addresses are configured? Will there ever be a need to send
518 // ARP requests from the controller in that case?
519 // All-zero MAC address doesn't seem to work - hosts don't respond to it
520
521 byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
522 byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
523 byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
524 byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
525 (byte) 0xff, (byte) 0xff, (byte) 0xff};
526
527 ARP arpRequest = new ARP();
528
529 arpRequest
530 .setHardwareType(ARP.HW_TYPE_ETHERNET)
531 .setProtocolType(ARP.PROTO_TYPE_IP)
532 .setHardwareAddressLength(
533 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
534 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
535 .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
536 .setTargetProtocolAddress(ipAddress.getAddress());
537
538 MACAddress routerMacAddress = configService.getRouterMacAddress();
TeruU3c049c42014-04-15 10:13:25 -0700539 // As for now, it's unclear what the MAC address should be
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700540 byte[] senderMacAddress = genericNonZeroMac;
541 if (routerMacAddress != null) {
542 senderMacAddress = routerMacAddress.toBytes();
543 }
544 arpRequest.setSenderHardwareAddress(senderMacAddress);
545
546 byte[] senderIPAddress = zeroIpv4;
547 Interface intf = configService.getOutgoingInterface(ipAddress);
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700548 if (intf == null) {
549 // TODO handle the case where the controller needs to send an ARP
550 // request but there's not IP configuration. In this case the
551 // request should be broadcast out all edge ports in the network.
pingping-lin6caee5f2014-08-25 11:39:32 -0700552 log.warn("Cannot send ARP: there is no outgoing interface in the "
553 + "configuration");
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700554 return;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700555 }
556
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700557 senderIPAddress = intf.getIpAddress().getAddress();
558
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700559 arpRequest.setSenderProtocolAddress(senderIPAddress);
560
561 Ethernet eth = new Ethernet();
562 eth.setSourceMACAddress(senderMacAddress)
563 .setDestinationMACAddress(broadcastMac)
564 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
565
566 if (vlan != NO_VLAN) {
567 eth.setVlanID(vlan).setPriorityCode((byte) 0);
568 }
569
570 // sendArpRequestToSwitches(ipAddress, eth.serialize());
TeruU3c049c42014-04-15 10:13:25 -0700571
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700572 packetService.sendPacket(
Jonathan Harte3702f22014-04-29 02:56:56 -0700573 eth, new SwitchPort(intf.getDpid(), intf.getPort()));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700574 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700575
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700576 // Please leave it for now because this code is needed for SDN-IP.
577 // It will be removed soon.
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700578 /*
TeruU7feef8a2014-04-03 00:15:49 -0700579 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
TeruU3c049c42014-04-15 10:13:25 -0700580 sendArpRequestToSwitches(dstAddress, arpRequest,
581 0, OFPort.OFPP_NONE.getValue());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700582 }
583
TeruU3c049c42014-04-15 10:13:25 -0700584 private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
585 long inSwitch, short inPort) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700586
587 if (configService.hasLayer3Configuration()) {
588 Interface intf = configService.getOutgoingInterface(dstAddress);
TeruU3c049c42014-04-15 10:13:25 -0700589 if (intf != null) {
590 sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
591 }
592 else {
593 //TODO here it should be broadcast out all non-interface edge ports.
594 //I think we can assume that if it's not a request for an external
595 //network, it's an ARP for a host in our own network. So we want to
596 //send it out all edge ports that don't have an interface configured
597 //to ensure it reaches all hosts in our network.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700598 log.debug("No interface found to send ARP request for {}",
599 dstAddress.getHostAddress());
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700600 }
TeruU3c049c42014-04-15 10:13:25 -0700601 }
602 else {
603 broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700604 }
605 }
Pavlin Radoslavovf1fdc7a2014-04-10 16:05:28 -0700606 */
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700607
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700608 private void sendArpReplyNotification(Ethernet eth) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700609 ARP arp = (ARP) eth.getPayload();
610
611 if (log.isTraceEnabled()) {
612 log.trace("Sending ARP reply for {} to other ONOS instances",
613 inetAddressToString(arp.getSenderProtocolAddress()));
614 }
615
616 InetAddress targetAddress;
617
618 try {
619 targetAddress = InetAddress.getByAddress(arp
620 .getSenderProtocolAddress());
621 } catch (UnknownHostException e) {
622 log.error("Unknown host", e);
623 return;
624 }
625
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700626 int intAddress = InetAddresses.coerceToInteger(targetAddress);
627
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700628 MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
629
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700630 ArpReplyNotification value = new ArpReplyNotification(intAddress, mac);
631
632 log.debug("ArpReplyNotification ip {}, mac {}", intAddress, mac);
TeruU3c049c42014-04-15 10:13:25 -0700633 arpReplyEventChannel.addTransientEntry(mac.toLong(), value);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700634 }
635
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700636 private void sendArpReply(ARP arpRequest, long dpid, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700637 MACAddress targetMac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700638 if (log.isTraceEnabled()) {
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700639 log.trace("Sending reply {} => {} to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700640 new Object[]{
Jonathan Hart51dc5e12014-04-22 11:03:59 -0700641 inetAddressToString(arpRequest.getTargetProtocolAddress()),
642 targetMac, inetAddressToString(arpRequest
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700643 .getSenderProtocolAddress())});
644 }
645
646 ARP arpReply = new ARP();
647 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
648 .setProtocolType(ARP.PROTO_TYPE_IP)
649 .setHardwareAddressLength(
650 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
651 .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
652 .setOpCode(ARP.OP_REPLY)
653 .setSenderHardwareAddress(targetMac.toBytes())
654 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
655 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
656 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
657
658 Ethernet eth = new Ethernet();
659 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
660 .setSourceMACAddress(targetMac.toBytes())
661 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
662
663 if (vlan != NO_VLAN) {
664 eth.setVlanID(vlan).setPriorityCode((byte) 0);
665 }
666
Jonathan Harte3702f22014-04-29 02:56:56 -0700667 packetService.sendPacket(eth, new SwitchPort(dpid, port));
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700668 }
669
670 private String inetAddressToString(byte[] bytes) {
671 try {
672 return InetAddress.getByAddress(bytes).getHostAddress();
673 } catch (UnknownHostException e) {
674 log.debug("Invalid IP address", e);
675 return "";
676 }
677 }
678
679 /*
680 * IProxyArpService methods
681 */
682
683 @Override
684 public MACAddress getMacAddress(InetAddress ipAddress) {
TeruU3c049c42014-04-15 10:13:25 -0700685 return arpCache.lookup(ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700686 }
687
688 @Override
689 public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
Ray Milkey269ffb92014-04-03 14:43:30 -0700690 boolean retry) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700691 arpRequests.put(ipAddress, new ArpRequest(requester, retry));
692
693 // Sanity check to make sure we don't send a request for our own address
694 if (!configService.isInterfaceAddress(ipAddress)) {
695 sendArpRequestForAddress(ipAddress);
696 }
697 }
698
699 @Override
700 public List<String> getMappings() {
Jonathan Hartf2519ee2014-05-09 10:51:45 -0700701 return arpCache.getMappings();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700702 }
703
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700704 private void sendArpReplyToWaitingRequesters(InetAddress address,
Ray Milkey269ffb92014-04-03 14:43:30 -0700705 MACAddress mac) {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700706 log.debug("Sending ARP reply for {} to requesters",
707 address.getHostAddress());
708
709 // See if anyone's waiting for this ARP reply
710 Set<ArpRequest> requests = arpRequests.get(address);
711
712 // Synchronize on the Multimap while using an iterator for one of the
713 // sets
714 List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
715 requests.size());
716 synchronized (arpRequests) {
717 Iterator<ArpRequest> it = requests.iterator();
718 while (it.hasNext()) {
719 ArpRequest request = it.next();
720 it.remove();
721 requestsToSend.add(request);
722 }
723 }
724
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700725 // Don't hold an ARP lock while dispatching requests
726 for (ArpRequest request : requestsToSend) {
727 request.dispatchReply(address, mac);
728 }
729 }
TeruU3c049c42014-04-15 10:13:25 -0700730
731 private void doPeriodicArpCleaning() {
732 List<InetAddress> expiredipslist = arpCache.getExpiredArpCacheIps();
733 for (InetAddress expireIp : expiredipslist) {
734 log.debug("call arpCacheEventChannel.removeEntry, ip {}", expireIp);
735 arpCacheEventChannel.removeEntry(expireIp.toString());
736 }
737 }
TeruU8b2d1672014-04-25 17:02:56 -0700738
739 public long getArpEntryTimeout() {
740 return arpCache.getArpEntryTimeout();
741 }
742
743 public long getArpCleaningTimerPeriod() {
744 return arpCleaningTimerPeriodConfig;
745 }
Yuta HIGUCHI33a04972014-06-03 13:00:15 -0700746
747 /**
748 * Replaces the internal ArpCache.
749 *
750 * @param cache ArpCache instance
751 *
752 * @exclude Backdoor for unit testing purpose only, do not use.
753 */
754 void debugReplaceArpCache(final ArpCache cache) {
755 this.arpCache = cache;
756 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700757}