blob: 39dc119c54f81a13ad1cbd898509cff2a8977bd7 [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 Hart5afde492013-10-01 12:30:53 +13005import java.util.List;
Jonathan Hartabad6a52013-09-30 18:17:21 +13006import java.util.Map;
TeruU3c049c42014-04-15 10:13:25 -07007import java.util.Map.Entry;
8import java.util.concurrent.ConcurrentHashMap;
9import java.util.concurrent.ConcurrentMap;
Jonathan Hartabad6a52013-09-30 18:17:21 +130010
11import net.floodlightcontroller.util.MACAddress;
TeruU3c049c42014-04-15 10:13:25 -070012import net.onrc.onos.core.datastore.KVArpCache;
Jonathan Hartabad6a52013-09-30 18:17:21 +130013
14import org.slf4j.Logger;
15import org.slf4j.LoggerFactory;
16
17/**
18 * Implements a basic ARP cache which maps IPv4 addresses to MAC addresses.
19 * Mappings time out after a short period of time (currently 1 min). We don't
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070020 * try and refresh the mapping before the entry times out because as a
21 * controller we don't know if the mapping is still needed.
Jonathan Hartabad6a52013-09-30 18:17:21 +130022 */
Jonathan Hart5afde492013-10-01 12:30:53 +130023class ArpCache {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070024 private static final Logger log = LoggerFactory.getLogger(ArpCache.class);
TeruU3c049c42014-04-15 10:13:25 -070025 private static long arpEntryTimeoutConfig = 60000; // ms (1 min)
26 private final KVArpCache kvArpCache;
Jonathan Hartabad6a52013-09-30 18:17:21 +130027
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070028 // Protected by locking on the ArpCache object (this)
TeruU3c049c42014-04-15 10:13:25 -070029 private final ConcurrentMap<InetAddress, ArpCacheEntry> arpCache;
Jonathan Hartabad6a52013-09-30 18:17:21 +130030
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070031 /**
32 * Represents a MAC address entry with a timestamp in the ARP cache.
33 * ARP cache entries are considered invalid if their timestamp is older
34 * than a timeout value.
35 */
36 private static class ArpCacheEntry {
37 private final MACAddress macAddress;
38 private long timeLastSeen;
Jonathan Hartabad6a52013-09-30 18:17:21 +130039
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070040 /**
41 * Class constructor, specifying the MAC address for the entry.
Ray Milkey269ffb92014-04-03 14:43:30 -070042 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070043 * @param macAddress MAC address for the entry
44 */
45 public ArpCacheEntry(MACAddress macAddress) {
46 this.macAddress = macAddress;
47 this.timeLastSeen = System.currentTimeMillis();
48 }
Jonathan Hartabad6a52013-09-30 18:17:21 +130049
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070050 /**
51 * Returns the MAC address this entry represents.
Ray Milkey269ffb92014-04-03 14:43:30 -070052 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070053 * @return this entry's MAC address
54 */
55 public MACAddress getMacAddress() {
56 return macAddress;
57 }
58
59 /**
60 * Update the timestamp for this entry.
Ray Milkey269ffb92014-04-03 14:43:30 -070061 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070062 * @param time the new timestamp to update the entry with
63 */
64 public void setTimeLastSeen(long time) {
65 timeLastSeen = time;
66 }
67
68 /**
69 * Returns whether the entry has timed out or not.
Ray Milkey269ffb92014-04-03 14:43:30 -070070 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070071 * @return true if the entry has timed out.
72 */
73 public boolean isExpired() {
TeruU3c049c42014-04-15 10:13:25 -070074 return System.currentTimeMillis() - timeLastSeen > arpEntryTimeoutConfig;
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070075 }
76 }
77
78 /**
TeruU3c049c42014-04-15 10:13:25 -070079 * Class constructors.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070080 */
TeruU3c049c42014-04-15 10:13:25 -070081 public ArpCache() {
82 arpCache = new ConcurrentHashMap<InetAddress, ArpCacheEntry>();
83 kvArpCache = new KVArpCache();
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070084 }
85
TeruU3c049c42014-04-15 10:13:25 -070086 public void setArpEntryTimeoutConfig(long arpEntryTimeout) {
87 if (arpEntryTimeout <= 0) {
88 throw new IllegalArgumentException(Long.toString(arpEntryTimeout));
89 }
90 arpEntryTimeoutConfig(arpEntryTimeout);
91 }
92
93 private static void arpEntryTimeoutConfig(long arpEntryTimeout) {
94 ArpCache.arpEntryTimeoutConfig = arpEntryTimeout;
95 log.debug("Set arpEntryTimeoutConfig {}", ArpCache.arpEntryTimeoutConfig);
96 }
97
TeruU8b2d1672014-04-25 17:02:56 -070098 public long getArpEntryTimeout() {
99 return ArpCache.arpEntryTimeoutConfig;
100 }
101
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700102 /**
103 * Get the MAC address that is mapped to an IP address in the ARP cache.
Ray Milkey269ffb92014-04-03 14:43:30 -0700104 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700105 * @param ipAddress the IP address to look up
106 * @return the MAC address if found in the cache, null if not
107 */
108 synchronized MACAddress lookup(InetAddress ipAddress) {
109 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
110
111 if (arpEntry == null) {
112 return null;
113 }
114
115 if (arpEntry.isExpired()) {
116 // Entry has timed out so we'll remove it and return null
117 log.trace("Removing expired ARP entry for {}",
118 ipAddress.getHostAddress());
119
120 arpCache.remove(ipAddress);
121 return null;
122 }
123
124 return arpEntry.getMacAddress();
125 }
126
127 /**
128 * Update an entry in the ARP cache. If the IP to MAC mapping is already
129 * in the cache, its timestamp will be updated. If not, the entry will
130 * be added with a new timestamp of the current time.
Ray Milkey269ffb92014-04-03 14:43:30 -0700131 *
132 * @param ipAddress the IP address that will be mapped in the cache
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700133 * @param macAddress the MAC address that maps to {@code ipAddress}
134 */
135 synchronized void update(InetAddress ipAddress, MACAddress macAddress) {
136 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
137
138 if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)) {
139 arpEntry.setTimeLastSeen(System.currentTimeMillis());
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700140 log.debug("The same ArpCache, ip {}, mac {}. " +
141 "Update local cache last seen time only.", ipAddress, macAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700142 } else {
143 arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
TeruU3c049c42014-04-15 10:13:25 -0700144 kvArpCache.forceCreate(ipAddress, macAddress.toBytes());
145 log.debug("Create/Update ip {}, mac {} in ArpCache.", ipAddress, macAddress);
146 }
147 }
148
149 /**
150 * Remove an entry in the ARP cache.
151 *
152 * @param ipAddress the IP address that will be mapped in the cache
153 */
154 synchronized void remove(InetAddress ipAddress) {
155 ArpCacheEntry entry = arpCache.remove(ipAddress);
156
157 if (entry == null) {
158 log.debug("ArpCache doesn't have the ip key {}.", ipAddress);
159 } else {
160 kvArpCache.forceDelete(ipAddress);
161 log.debug("Remove it in ArpCache and DB, ip {}", ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700162 }
163 }
164
165 /**
166 * Retrieve a list of all mappings in the ARP cache.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700167 *
Ray Milkey269ffb92014-04-03 14:43:30 -0700168 * @return list of all ARP mappings, formatted as a human-readable string
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700169 */
TeruU3c049c42014-04-15 10:13:25 -0700170 List<String> getMappings() {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700171 List<String> result = new ArrayList<String>(arpCache.size());
172
173 for (Map.Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
174 result.add(entry.getKey().getHostAddress()
175 + " => "
176 + entry.getValue().getMacAddress().toString()
177 + (entry.getValue().isExpired() ? " : EXPIRED" : " : VALID"));
178 }
179
180 return result;
181 }
TeruU3c049c42014-04-15 10:13:25 -0700182
183 /**
184 * Retrieve a list of all expired IPs in the ARP cache.
185 *
186 * @return list of all expired IPs
187 */
188 List<InetAddress> getExpiredArpCacheIps() {
189 List<InetAddress> result = new ArrayList<InetAddress>();
190
191 for (Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
192 if (entry.getValue().isExpired()) {
193 log.debug("add to the expired ip list, ip {}", entry.getKey());
194 result.add(entry.getKey());
195 }
196 }
197
198 return result;
199 }
Jonathan Hartabad6a52013-09-30 18:17:21 +1300200}