blob: 0e965ff615e2f576077328913c63f454446ab2ab [file] [log] [blame]
Jonathan Hartabad6a52013-09-30 18:17:21 +13001package net.onrc.onos.ofcontroller.proxyarp;
2
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.
46 * @param macAddress MAC address for the entry
47 */
48 public ArpCacheEntry(MACAddress macAddress) {
49 this.macAddress = macAddress;
50 this.timeLastSeen = System.currentTimeMillis();
51 }
Jonathan Hartabad6a52013-09-30 18:17:21 +130052
Jonathan Hart7c9a2fb2014-03-27 09:51:41 -070053 /**
54 * Returns the MAC address this entry represents.
55 * @return this entry's MAC address
56 */
57 public MACAddress getMacAddress() {
58 return macAddress;
59 }
60
61 /**
62 * Update the timestamp for this entry.
63 * @param time the new timestamp to update the entry with
64 */
65 public void setTimeLastSeen(long time) {
66 timeLastSeen = time;
67 }
68
69 /**
70 * Returns whether the entry has timed out or not.
71 * @return true if the entry has timed out.
72 */
73 public boolean isExpired() {
74 return System.currentTimeMillis() - timeLastSeen > ARP_ENTRY_TIMEOUT;
75 }
76 }
77
78 /**
79 * Class constructor.
80 */
81 ArpCache() {
82 arpCache = new HashMap<InetAddress, ArpCacheEntry>();
83 }
84
85 /**
86 * Get the MAC address that is mapped to an IP address in the ARP cache.
87 * @param ipAddress the IP address to look up
88 * @return the MAC address if found in the cache, null if not
89 */
90 synchronized MACAddress lookup(InetAddress ipAddress) {
91 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
92
93 if (arpEntry == null) {
94 return null;
95 }
96
97 if (arpEntry.isExpired()) {
98 // Entry has timed out so we'll remove it and return null
99 log.trace("Removing expired ARP entry for {}",
100 ipAddress.getHostAddress());
101
102 arpCache.remove(ipAddress);
103 return null;
104 }
105
106 return arpEntry.getMacAddress();
107 }
108
109 /**
110 * Update an entry in the ARP cache. If the IP to MAC mapping is already
111 * in the cache, its timestamp will be updated. If not, the entry will
112 * be added with a new timestamp of the current time.
113 * @param ipAddress the IP address that will be mapped in the cache
114 * @param macAddress the MAC address that maps to {@code ipAddress}
115 */
116 synchronized void update(InetAddress ipAddress, MACAddress macAddress) {
117 ArpCacheEntry arpEntry = arpCache.get(ipAddress);
118
119 if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)) {
120 arpEntry.setTimeLastSeen(System.currentTimeMillis());
121 } else {
122 arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
123 }
124 }
125
126 /**
127 * Retrieve a list of all mappings in the ARP cache.
128 * @return list of all ARP mappings, formatted as a human-readable string
129 *
130 */
131 synchronized List<String> getMappings() {
132 List<String> result = new ArrayList<String>(arpCache.size());
133
134 for (Map.Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
135 result.add(entry.getKey().getHostAddress()
136 + " => "
137 + entry.getValue().getMacAddress().toString()
138 + (entry.getValue().isExpired() ? " : EXPIRED" : " : VALID"));
139 }
140
141 return result;
142 }
Jonathan Hartabad6a52013-09-30 18:17:21 +1300143}