blob: f8c158997d0169993a7473dc6f948f45624697a1 [file] [log] [blame]
Jonathan Hart0961fe82014-04-03 09:56:25 -07001package net.onrc.onos.apps.proxyarp;
Jonathan Hartabad6a52013-09-30 18:17:21 +13002
3import java.net.InetAddress;
Jonathan Hart5afde492013-10-01 12:30:53 +13004import java.util.ArrayList;
Jonathan Hartabad6a52013-09-30 18:17:21 +13005import java.util.HashMap;
Jonathan Hart5afde492013-10-01 12:30:53 +13006import java.util.List;
Jonathan Hartabad6a52013-09-30 18:17:21 +13007import java.util.Map;
8
9import net.floodlightcontroller.util.MACAddress;
10
11import org.slf4j.Logger;
12import org.slf4j.LoggerFactory;
13
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070014/*
15 * TODO clean out old ARP entries out of the cache periodically. We currently
16 * don't do this which means the cache memory size will never decrease. We
17 * already have a periodic thread that can be used to do this in
18 * ProxyArpManager.
19 */
20
Jonathan Hartabad6a52013-09-30 18:17:21 +130021/**
22 * Implements a basic ARP cache which maps IPv4 addresses to MAC addresses.
23 * Mappings time out after a short period of time (currently 1 min). We don't
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070024 * try and refresh the mapping before the entry times out because as a
25 * controller we don't know if the mapping is still needed.
Jonathan Hartabad6a52013-09-30 18:17:21 +130026 */
Jonathan Hart5afde492013-10-01 12:30:53 +130027class ArpCache {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070028 private static final Logger log = LoggerFactory.getLogger(ArpCache.class);
Jonathan Hartabad6a52013-09-30 18:17:21 +130029
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070030 private static final long ARP_ENTRY_TIMEOUT = 60000; // ms (1 min)
Jonathan Hartabad6a52013-09-30 18:17:21 +130031
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070032 // Protected by locking on the ArpCache object (this)
33 private final Map<InetAddress, ArpCacheEntry> arpCache;
Jonathan Hartabad6a52013-09-30 18:17:21 +130034
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070035 /**
36 * Represents a MAC address entry with a timestamp in the ARP cache.
37 * ARP cache entries are considered invalid if their timestamp is older
38 * than a timeout value.
39 */
40 private static class ArpCacheEntry {
41 private final MACAddress macAddress;
42 private long timeLastSeen;
Jonathan Hartabad6a52013-09-30 18:17:21 +130043
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070044 /**
45 * Class constructor, specifying the MAC address for the entry.
Ray Milkey269ffb92014-04-03 14:43:30 -070046 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070047 * @param macAddress MAC address for the entry
48 */
49 public ArpCacheEntry(MACAddress macAddress) {
50 this.macAddress = macAddress;
51 this.timeLastSeen = System.currentTimeMillis();
52 }
Jonathan Hartabad6a52013-09-30 18:17:21 +130053
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070054 /**
55 * Returns the MAC address this entry represents.
Ray Milkey269ffb92014-04-03 14:43:30 -070056 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070057 * @return this entry's MAC address
58 */
59 public MACAddress getMacAddress() {
60 return macAddress;
61 }
62
63 /**
64 * Update the timestamp for this entry.
Ray Milkey269ffb92014-04-03 14:43:30 -070065 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070066 * @param time the new timestamp to update the entry with
67 */
68 public void setTimeLastSeen(long time) {
69 timeLastSeen = time;
70 }
71
72 /**
73 * Returns whether the entry has timed out or not.
Ray Milkey269ffb92014-04-03 14:43:30 -070074 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070075 * @return true if the entry has timed out.
76 */
77 public boolean isExpired() {
78 return System.currentTimeMillis() - timeLastSeen > ARP_ENTRY_TIMEOUT;
79 }
80 }
81
82 /**
83 * Class constructor.
84 */
85 ArpCache() {
86 arpCache = new HashMap<InetAddress, ArpCacheEntry>();
87 }
88
89 /**
90 * Get the MAC address that is mapped to an IP address in the ARP cache.
Ray Milkey269ffb92014-04-03 14:43:30 -070091 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070092 * @param ipAddress the IP address to look up
93 * @return the MAC address if found in the cache, null if not
94 */
95 synchronized MACAddress lookup(InetAddress ipAddress) {
96 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
97
98 if (arpEntry == null) {
99 return null;
100 }
101
102 if (arpEntry.isExpired()) {
103 // Entry has timed out so we'll remove it and return null
104 log.trace("Removing expired ARP entry for {}",
105 ipAddress.getHostAddress());
106
107 arpCache.remove(ipAddress);
108 return null;
109 }
110
111 return arpEntry.getMacAddress();
112 }
113
114 /**
115 * Update an entry in the ARP cache. If the IP to MAC mapping is already
116 * in the cache, its timestamp will be updated. If not, the entry will
117 * be added with a new timestamp of the current time.
Ray Milkey269ffb92014-04-03 14:43:30 -0700118 *
119 * @param ipAddress the IP address that will be mapped in the cache
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700120 * @param macAddress the MAC address that maps to {@code ipAddress}
121 */
122 synchronized void update(InetAddress ipAddress, MACAddress macAddress) {
123 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
124
125 if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)) {
126 arpEntry.setTimeLastSeen(System.currentTimeMillis());
127 } else {
128 arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
129 }
130 }
131
132 /**
133 * Retrieve a list of all mappings in the ARP cache.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700134 *
Ray Milkey269ffb92014-04-03 14:43:30 -0700135 * @return list of all ARP mappings, formatted as a human-readable string
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700136 */
137 synchronized List<String> getMappings() {
138 List<String> result = new ArrayList<String>(arpCache.size());
139
140 for (Map.Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
141 result.add(entry.getKey().getHostAddress()
142 + " => "
143 + entry.getValue().getMacAddress().toString()
144 + (entry.getValue().isExpired() ? " : EXPIRED" : " : VALID"));
145 }
146
147 return result;
148 }
Jonathan Hartabad6a52013-09-30 18:17:21 +1300149}