blob: 38cd41fa39d36d106ccf060451d4023260eaf032 [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
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070098 /**
99 * Get the MAC address that is mapped to an IP address in the ARP cache.
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 *
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700101 * @param ipAddress the IP address to look up
102 * @return the MAC address if found in the cache, null if not
103 */
104 synchronized MACAddress lookup(InetAddress ipAddress) {
105 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
106
107 if (arpEntry == null) {
108 return null;
109 }
110
111 if (arpEntry.isExpired()) {
112 // Entry has timed out so we'll remove it and return null
113 log.trace("Removing expired ARP entry for {}",
114 ipAddress.getHostAddress());
115
116 arpCache.remove(ipAddress);
117 return null;
118 }
119
120 return arpEntry.getMacAddress();
121 }
122
123 /**
124 * Update an entry in the ARP cache. If the IP to MAC mapping is already
125 * in the cache, its timestamp will be updated. If not, the entry will
126 * be added with a new timestamp of the current time.
Ray Milkey269ffb92014-04-03 14:43:30 -0700127 *
128 * @param ipAddress the IP address that will be mapped in the cache
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700129 * @param macAddress the MAC address that maps to {@code ipAddress}
130 */
131 synchronized void update(InetAddress ipAddress, MACAddress macAddress) {
132 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
133
134 if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)) {
135 arpEntry.setTimeLastSeen(System.currentTimeMillis());
TeruU3c049c42014-04-15 10:13:25 -0700136 log.debug("The same ArpCache, ip {}, mac {}. Update local cache last seen time only.", ipAddress, macAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700137 } else {
138 arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
TeruU3c049c42014-04-15 10:13:25 -0700139 kvArpCache.forceCreate(ipAddress, macAddress.toBytes());
140 log.debug("Create/Update ip {}, mac {} in ArpCache.", ipAddress, macAddress);
141 }
142 }
143
144 /**
145 * Remove an entry in the ARP cache.
146 *
147 * @param ipAddress the IP address that will be mapped in the cache
148 */
149 synchronized void remove(InetAddress ipAddress) {
150 ArpCacheEntry entry = arpCache.remove(ipAddress);
151
152 if (entry == null) {
153 log.debug("ArpCache doesn't have the ip key {}.", ipAddress);
154 } else {
155 kvArpCache.forceDelete(ipAddress);
156 log.debug("Remove it in ArpCache and DB, ip {}", ipAddress);
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700157 }
158 }
159
160 /**
161 * Retrieve a list of all mappings in the ARP cache.
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700162 *
Ray Milkey269ffb92014-04-03 14:43:30 -0700163 * @return list of all ARP mappings, formatted as a human-readable string
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700164 */
TeruU3c049c42014-04-15 10:13:25 -0700165 List<String> getMappings() {
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -0700166 List<String> result = new ArrayList<String>(arpCache.size());
167
168 for (Map.Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
169 result.add(entry.getKey().getHostAddress()
170 + " => "
171 + entry.getValue().getMacAddress().toString()
172 + (entry.getValue().isExpired() ? " : EXPIRED" : " : VALID"));
173 }
174
175 return result;
176 }
TeruU3c049c42014-04-15 10:13:25 -0700177
178 /**
179 * Retrieve a list of all expired IPs in the ARP cache.
180 *
181 * @return list of all expired IPs
182 */
183 List<InetAddress> getExpiredArpCacheIps() {
184 List<InetAddress> result = new ArrayList<InetAddress>();
185
186 for (Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
187 if (entry.getValue().isExpired()) {
188 log.debug("add to the expired ip list, ip {}", entry.getKey());
189 result.add(entry.getKey());
190 }
191 }
192
193 return result;
194 }
Jonathan Hartabad6a52013-09-30 18:17:21 +1300195}