blob: 99c8769ebc96deb5f6b984e57cf65db64cbbf6d9 [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.devicemanager;
Jonathan Hartd857ad62013-12-14 18:08:17 -08002
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Date;
6import java.util.HashMap;
TeruU80ce5062014-03-03 17:16:13 -08007import java.util.HashSet;
Jonathan Hartd857ad62013-12-14 18:08:17 -08008import java.util.List;
9import java.util.Map;
TeruU80ce5062014-03-03 17:16:13 -080010import java.util.Set;
11import java.util.concurrent.ConcurrentHashMap;
TeruUd1c5b652014-03-24 13:58:46 -070012import java.util.concurrent.CopyOnWriteArrayList;
TeruU80ce5062014-03-03 17:16:13 -080013import java.util.concurrent.Executors;
14import java.util.concurrent.ScheduledExecutorService;
15import java.util.concurrent.TimeUnit;
Jonathan Hartd857ad62013-12-14 18:08:17 -080016
17import net.floodlightcontroller.core.FloodlightContext;
18import net.floodlightcontroller.core.IFloodlightProviderService;
19import net.floodlightcontroller.core.IOFMessageListener;
20import net.floodlightcontroller.core.IOFSwitch;
21import net.floodlightcontroller.core.IUpdate;
22import net.floodlightcontroller.core.module.FloodlightModuleContext;
23import net.floodlightcontroller.core.module.FloodlightModuleException;
24import net.floodlightcontroller.core.module.IFloodlightModule;
25import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hartd857ad62013-12-14 18:08:17 -080026import net.floodlightcontroller.util.MACAddress;
Jonathan Hart6df90172014-04-03 10:13:11 -070027import net.onrc.onos.core.datagrid.IDatagridService;
28import net.onrc.onos.core.datagrid.IEventChannel;
29import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070030import net.onrc.onos.core.packet.ARP;
31import net.onrc.onos.core.packet.DHCP;
32import net.onrc.onos.core.packet.Ethernet;
33import net.onrc.onos.core.packet.IPv4;
34import net.onrc.onos.core.packet.UDP;
Jonathan Hart472062d2014-04-03 10:56:48 -070035import net.onrc.onos.core.topology.INetworkGraphService;
36import net.onrc.onos.core.topology.NetworkGraph;
Jonathan Hartd857ad62013-12-14 18:08:17 -080037
38import org.openflow.protocol.OFMessage;
39import org.openflow.protocol.OFPacketIn;
40import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080043
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070044public class OnosDeviceManager implements IFloodlightModule,
Ray Milkey269ffb92014-04-03 14:43:30 -070045 IOFMessageListener,
46 IOnosDeviceService,
47 IEventChannelListener<Long, OnosDevice> {
Ray Milkeyec838942014-04-09 11:28:43 -070048 private static final Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
Ray Milkey269ffb92014-04-03 14:43:30 -070049 private static final int CLEANUP_SECOND = 60 * 60;
50 private static final int AGEING_MILLSEC = 60 * 60 * 1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070051
Ray Milkey269ffb92014-04-03 14:43:30 -070052 private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
53 private IFloodlightProviderService floodlightProvider;
Ray Milkeyec838942014-04-09 11:28:43 -070054 private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070055
Ray Milkey269ffb92014-04-03 14:43:30 -070056 private IDatagridService datagrid;
57 private IEventChannel<Long, OnosDevice> eventChannel;
58 private static final String DEVICE_CHANNEL_NAME = "onos.device";
59 private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
60 private INetworkGraphService networkGraphService;
61 private NetworkGraph networkGraph;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070062
TeruU80ce5062014-03-03 17:16:13 -080063 public enum OnosDeviceUpdateType {
64 ADD, DELETE, UPDATE;
65 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070066
Ray Milkey269ffb92014-04-03 14:43:30 -070067 private class OnosDeviceUpdate implements IUpdate {
68 private OnosDevice device;
69 private OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070070
Ray Milkey269ffb92014-04-03 14:43:30 -070071 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
72 this.device = device;
73 this.type = type;
74 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070075
Ray Milkey269ffb92014-04-03 14:43:30 -070076 @Override
77 public void dispatch() {
78 if (type == OnosDeviceUpdateType.ADD) {
79 for (IOnosDeviceListener listener : deviceListeners) {
80 listener.onosDeviceAdded(device);
81 }
82 } else if (type == OnosDeviceUpdateType.DELETE) {
83 for (IOnosDeviceListener listener : deviceListeners) {
84 listener.onosDeviceRemoved(device);
85 }
86 }
87 }
88 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070089
Ray Milkey269ffb92014-04-03 14:43:30 -070090 @Override
91 public String getName() {
92 return "onosdevicemanager";
93 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080094
Ray Milkey269ffb92014-04-03 14:43:30 -070095 @Override
96 public boolean isCallbackOrderingPrereq(OFType type, String name) {
97 // We want link discovery to consume LLDP first otherwise we'll
98 // end up reading bad device info from LLDP packets
99 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
100 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800101
Ray Milkey269ffb92014-04-03 14:43:30 -0700102 @Override
103 public boolean isCallbackOrderingPostreq(OFType type, String name) {
104 return type == OFType.PACKET_IN &&
105 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
106 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800107
Ray Milkey269ffb92014-04-03 14:43:30 -0700108 @Override
109 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700110 if (msg.getType().equals(OFType.PACKET_IN) &&
111 (msg instanceof OFPacketIn)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700112 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700113
Ray Milkey269ffb92014-04-03 14:43:30 -0700114 Ethernet eth = IFloodlightProviderService.bcStore.
115 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700116
Ray Milkey269ffb92014-04-03 14:43:30 -0700117 return processPacketIn(sw, pi, eth);
118 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700119
Ray Milkey269ffb92014-04-03 14:43:30 -0700120 return Command.CONTINUE;
121 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700122
Ray Milkey269ffb92014-04-03 14:43:30 -0700123 private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
124 long dpid = sw.getId();
TeruU80ce5062014-03-03 17:16:13 -0800125 short portId = pi.getInPort();
126 Long mac = eth.getSourceMAC().toLong();
127
Jonathan Hartd857ad62013-12-14 18:08:17 -0800128 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800129 getSourceDeviceFromPacket(eth, dpid, portId);
130
Ray Milkey269ffb92014-04-03 14:43:30 -0700131 if (srcDevice == null) {
132 return Command.STOP;
TeruU80ce5062014-03-03 17:16:13 -0800133 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700134
TeruU80ce5062014-03-03 17:16:13 -0800135 //We check if it is the same device in datagrid to suppress the device update
Ray Milkeyb6e0ac82014-04-09 13:21:42 -0700136 OnosDevice exDev = mapDevice.get(mac);
137 if (exDev != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700138 if (exDev.equals(srcDevice)) {
139 //There is the same existing device. Update only ActiveSince time.
140 exDev.setLastSeenTimestamp(new Date());
141 if (log.isTraceEnabled()) {
142 log.debug("In the datagrid, there is the same device."
143 + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
144 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
145 }
146 return Command.CONTINUE;
147 } else if (srcDevice.getIpv4Address() == null &&
148 exDev.getSwitchDPID().equals(srcDevice.getSwitchDPID()) &&
149 exDev.getSwitchPort() == srcDevice.getSwitchPort()) {
150 //Vlan should be handled based on the Onos spec. Until then, don't handle it.
151 //Device attachment point and mac address are the same
152 //but the packet does not have an ip address.
153 exDev.setLastSeenTimestamp(new Date());
154 if (log.isTraceEnabled()) {
155 log.debug("In the datagrid, there is the same device with no ip."
156 + "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {} lastSeenTime {}",
157 dpid, portId, srcDevice.getMacAddress(), exDev.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
158 }
159 return Command.CONTINUE;
160 }
161 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700162
TeruU80ce5062014-03-03 17:16:13 -0800163 //If the switch port we try to attach a new device already has a link, then stop adding device
Ray Milkey269ffb92014-04-03 14:43:30 -0700164 if (networkGraph.getLink(dpid, (long) portId) != null) {
165 if (log.isTraceEnabled()) {
166 log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
167 srcDevice.getMacAddress(), dpid, portId);
168 }
169 return Command.CONTINUE;
TeruU80ce5062014-03-03 17:16:13 -0800170 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700171
TeruU80ce5062014-03-03 17:16:13 -0800172 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700173
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 if (log.isTraceEnabled()) {
175 log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
176 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
TeruU80ce5062014-03-03 17:16:13 -0800177 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800178 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700179 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700180
Ray Milkey269ffb92014-04-03 14:43:30 -0700181 //Thread to delete devices periodically.
182 //Remove all devices from the map first and then finally delete devices from the DB.
183 private class CleanDevice implements Runnable {
184 @Override
185 public void run() {
186 log.debug("called CleanDevice");
187 try {
188 Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
189 for (OnosDevice dev : mapDevice.values()) {
190 long now = new Date().getTime();
191 if ((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
192 if (log.isTraceEnabled()) {
193 log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
194 dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
195 dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
196 }
197 deleteSet.add(dev);
198 }
199 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700200
Ray Milkey269ffb92014-04-03 14:43:30 -0700201 for (OnosDevice dev : deleteSet) {
202 deleteOnosDevice(dev);
203 }
204 } catch (Exception e) {
205 log.error("Error:", e);
206 }
207 }
208 }
TeruU80ce5062014-03-03 17:16:13 -0800209
Jonathan Hartd857ad62013-12-14 18:08:17 -0800210 /**
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700211 * Get IP address from packet if the packet is either an ARP
Ray Milkeyb41100a2014-04-10 10:42:15 -0700212 * or a DHCP packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700213 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800214 * @param eth
215 * @param dlAddr
216 * @return
217 */
218 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
219 if (eth.getPayload() instanceof ARP) {
220 ARP arp = (ARP) eth.getPayload();
221 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
222 (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
223 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
224 }
225 } else if (eth.getPayload() instanceof IPv4) {
226 IPv4 ipv4 = (IPv4) eth.getPayload();
227 if (ipv4.getPayload() instanceof UDP) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700228 UDP udp = (UDP) ipv4.getPayload();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800229 if (udp.getPayload() instanceof DHCP) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700230 DHCP dhcp = (DHCP) udp.getPayload();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800231 if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
232 return ipv4.getSourceAddress();
233 }
234 }
235 }
236 }
237 return 0;
238 }
239
240 /**
241 * Parse an entity from an {@link Ethernet} packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700242 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800243 * @param eth the packet to parse
Ray Milkey269ffb92014-04-03 14:43:30 -0700244 * @param sw the switch on which the packet arrived
245 * @param pi the original packetin
Jonathan Hartd857ad62013-12-14 18:08:17 -0800246 * @return the entity from the packet
247 */
248 private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
Ray Milkey269ffb92014-04-03 14:43:30 -0700249 long swdpid,
250 short port) {
Jonathan Hartd857ad62013-12-14 18:08:17 -0800251 byte[] dlAddrArr = eth.getSourceMACAddress();
252 long dlAddr = Ethernet.toLong(dlAddrArr);
253
254 // Ignore broadcast/multicast source
Ray Milkeyb29e6262014-04-09 16:02:14 -0700255 if ((dlAddrArr[0] & 0x1) != 0) {
Jonathan Hartd857ad62013-12-14 18:08:17 -0800256 return null;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700257 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800258
259 short vlan = eth.getVlanID();
260 int nwSrc = getSrcNwAddr(eth, dlAddr);
261 return new OnosDevice(MACAddress.valueOf(dlAddr),
Ray Milkey269ffb92014-04-03 14:43:30 -0700262 ((vlan >= 0) ? vlan : null),
263 ((nwSrc != 0) ? nwSrc : null),
264 swdpid,
265 port,
266 new Date());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800267 }
268
Ray Milkey269ffb92014-04-03 14:43:30 -0700269 @Override
270 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
271 List<Class<? extends IFloodlightService>> services =
272 new ArrayList<Class<? extends IFloodlightService>>();
273 services.add(IOnosDeviceService.class);
274 return services;
275 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800276
TeruUd1c5b652014-03-24 13:58:46 -0700277 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700278 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
279 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
280 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
281 impls.put(IOnosDeviceService.class, this);
282 return impls;
283 }
284
TeruUd1c5b652014-03-24 13:58:46 -0700285 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700286 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
287 List<Class<? extends IFloodlightService>> dependencies =
288 new ArrayList<Class<? extends IFloodlightService>>();
289 dependencies.add(IFloodlightProviderService.class);
290 dependencies.add(INetworkGraphService.class);
291 dependencies.add(IDatagridService.class);
292 return dependencies;
293 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700294
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 @Override
296 public void init(FloodlightModuleContext context)
297 throws FloodlightModuleException {
298 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700299 EXECUTOR_SERVICE.scheduleAtFixedRate(new CleanDevice(), 30, CLEANUP_SECOND, TimeUnit.SECONDS);
TeruU80ce5062014-03-03 17:16:13 -0800300
Ray Milkey269ffb92014-04-03 14:43:30 -0700301 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
302 datagrid = context.getServiceImpl(IDatagridService.class);
303 networkGraphService = context.getServiceImpl(INetworkGraphService.class);
304 networkGraph = networkGraphService.getNetworkGraph();
305 }
TeruU80ce5062014-03-03 17:16:13 -0800306
Ray Milkey269ffb92014-04-03 14:43:30 -0700307 @Override
308 public void startUp(FloodlightModuleContext context) {
309 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
310 eventChannel = datagrid.addListener(DEVICE_CHANNEL_NAME, this,
311 Long.class,
312 OnosDevice.class);
313 }
TeruUd1c5b652014-03-24 13:58:46 -0700314
Ray Milkey269ffb92014-04-03 14:43:30 -0700315 @Override
316 public void deleteOnosDevice(OnosDevice dev) {
317 Long mac = dev.getMacAddress().toLong();
318 eventChannel.removeEntry(mac);
319 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
320 }
TeruUd1c5b652014-03-24 13:58:46 -0700321
Ray Milkey269ffb92014-04-03 14:43:30 -0700322 @Override
323 public void deleteOnosDeviceByMac(MACAddress mac) {
Pavlin Radoslavov63607792014-04-09 16:56:28 -0700324 OnosDevice deleteDevice = mapDevice.get(mac.toLong());
Ray Milkey269ffb92014-04-03 14:43:30 -0700325 deleteOnosDevice(deleteDevice);
326 }
327
328 @Override
329 public void addOnosDevice(Long mac, OnosDevice dev) {
330 eventChannel.addEntry(mac, dev);
331 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
332 }
333
334 @Override
335 public void entryAdded(OnosDevice dev) {
336 Long mac = dev.getMacAddress().toLong();
337 mapDevice.put(mac, dev);
338 log.debug("Device added: device mac {}", mac);
339 }
340
341 @Override
342 public void entryRemoved(OnosDevice dev) {
343 Long mac = dev.getMacAddress().toLong();
344 mapDevice.remove(mac);
345 log.debug("Device removed: device mac {}", mac);
346 }
347
348 @Override
349 public void entryUpdated(OnosDevice dev) {
350 Long mac = dev.getMacAddress().toLong();
351 mapDevice.put(mac, dev);
352 log.debug("Device updated: device mac {}", mac);
353 }
354
355 @Override
356 public void addOnosDeviceListener(IOnosDeviceListener listener) {
357 deviceListeners.add(listener);
358 }
359
360 @Override
361 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
362 deviceListeners.remove(listener);
363 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800364}