blob: 30ad722112888676b167204b8f128ad2443a668d [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) {
110 if (msg.getType().equals(OFType.PACKET_IN)) {
111 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700112
Ray Milkey269ffb92014-04-03 14:43:30 -0700113 Ethernet eth = IFloodlightProviderService.bcStore.
114 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700115
Ray Milkey269ffb92014-04-03 14:43:30 -0700116 return processPacketIn(sw, pi, eth);
117 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700118
Ray Milkey269ffb92014-04-03 14:43:30 -0700119 return Command.CONTINUE;
120 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700121
Ray Milkey269ffb92014-04-03 14:43:30 -0700122 private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
123 long dpid = sw.getId();
TeruU80ce5062014-03-03 17:16:13 -0800124 short portId = pi.getInPort();
125 Long mac = eth.getSourceMAC().toLong();
126
Jonathan Hartd857ad62013-12-14 18:08:17 -0800127 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800128 getSourceDeviceFromPacket(eth, dpid, portId);
129
Ray Milkey269ffb92014-04-03 14:43:30 -0700130 if (srcDevice == null) {
131 return Command.STOP;
TeruU80ce5062014-03-03 17:16:13 -0800132 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700133
TeruU80ce5062014-03-03 17:16:13 -0800134 //We check if it is the same device in datagrid to suppress the device update
Ray Milkeyb6e0ac82014-04-09 13:21:42 -0700135 OnosDevice exDev = mapDevice.get(mac);
136 if (exDev != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700137 if (exDev.equals(srcDevice)) {
138 //There is the same existing device. Update only ActiveSince time.
139 exDev.setLastSeenTimestamp(new Date());
140 if (log.isTraceEnabled()) {
141 log.debug("In the datagrid, there is the same device."
142 + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
143 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
144 }
145 return Command.CONTINUE;
146 } else if (srcDevice.getIpv4Address() == null &&
147 exDev.getSwitchDPID().equals(srcDevice.getSwitchDPID()) &&
148 exDev.getSwitchPort() == srcDevice.getSwitchPort()) {
149 //Vlan should be handled based on the Onos spec. Until then, don't handle it.
150 //Device attachment point and mac address are the same
151 //but the packet does not have an ip address.
152 exDev.setLastSeenTimestamp(new Date());
153 if (log.isTraceEnabled()) {
154 log.debug("In the datagrid, there is the same device with no ip."
155 + "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {} lastSeenTime {}",
156 dpid, portId, srcDevice.getMacAddress(), exDev.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
157 }
158 return Command.CONTINUE;
159 }
160 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700161
TeruU80ce5062014-03-03 17:16:13 -0800162 //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 -0700163 if (networkGraph.getLink(dpid, (long) portId) != null) {
164 if (log.isTraceEnabled()) {
165 log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
166 srcDevice.getMacAddress(), dpid, portId);
167 }
168 return Command.CONTINUE;
TeruU80ce5062014-03-03 17:16:13 -0800169 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700170
TeruU80ce5062014-03-03 17:16:13 -0800171 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700172
Ray Milkey269ffb92014-04-03 14:43:30 -0700173 if (log.isTraceEnabled()) {
174 log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
175 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
TeruU80ce5062014-03-03 17:16:13 -0800176 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800177 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700178 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700179
Ray Milkey269ffb92014-04-03 14:43:30 -0700180 //Thread to delete devices periodically.
181 //Remove all devices from the map first and then finally delete devices from the DB.
182 private class CleanDevice implements Runnable {
183 @Override
184 public void run() {
185 log.debug("called CleanDevice");
186 try {
187 Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
188 for (OnosDevice dev : mapDevice.values()) {
189 long now = new Date().getTime();
190 if ((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
191 if (log.isTraceEnabled()) {
192 log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
193 dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
194 dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
195 }
196 deleteSet.add(dev);
197 }
198 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700199
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 for (OnosDevice dev : deleteSet) {
201 deleteOnosDevice(dev);
202 }
203 } catch (Exception e) {
204 log.error("Error:", e);
205 }
206 }
207 }
TeruU80ce5062014-03-03 17:16:13 -0800208
Jonathan Hartd857ad62013-12-14 18:08:17 -0800209 /**
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700210 * Get IP address from packet if the packet is either an ARP
Jonathan Hartd857ad62013-12-14 18:08:17 -0800211 * or a DHCP packet
Ray Milkey269ffb92014-04-03 14:43:30 -0700212 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800213 * @param eth
214 * @param dlAddr
215 * @return
216 */
217 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
218 if (eth.getPayload() instanceof ARP) {
219 ARP arp = (ARP) eth.getPayload();
220 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
221 (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
222 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
223 }
224 } else if (eth.getPayload() instanceof IPv4) {
225 IPv4 ipv4 = (IPv4) eth.getPayload();
226 if (ipv4.getPayload() instanceof UDP) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700227 UDP udp = (UDP) ipv4.getPayload();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800228 if (udp.getPayload() instanceof DHCP) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700229 DHCP dhcp = (DHCP) udp.getPayload();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800230 if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
231 return ipv4.getSourceAddress();
232 }
233 }
234 }
235 }
236 return 0;
237 }
238
239 /**
240 * Parse an entity from an {@link Ethernet} packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700241 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800242 * @param eth the packet to parse
Ray Milkey269ffb92014-04-03 14:43:30 -0700243 * @param sw the switch on which the packet arrived
244 * @param pi the original packetin
Jonathan Hartd857ad62013-12-14 18:08:17 -0800245 * @return the entity from the packet
246 */
247 private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
Ray Milkey269ffb92014-04-03 14:43:30 -0700248 long swdpid,
249 short port) {
Jonathan Hartd857ad62013-12-14 18:08:17 -0800250 byte[] dlAddrArr = eth.getSourceMACAddress();
251 long dlAddr = Ethernet.toLong(dlAddrArr);
252
253 // Ignore broadcast/multicast source
254 if ((dlAddrArr[0] & 0x1) != 0)
255 return null;
256
257 short vlan = eth.getVlanID();
258 int nwSrc = getSrcNwAddr(eth, dlAddr);
259 return new OnosDevice(MACAddress.valueOf(dlAddr),
Ray Milkey269ffb92014-04-03 14:43:30 -0700260 ((vlan >= 0) ? vlan : null),
261 ((nwSrc != 0) ? nwSrc : null),
262 swdpid,
263 port,
264 new Date());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800265 }
266
Ray Milkey269ffb92014-04-03 14:43:30 -0700267 @Override
268 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
269 List<Class<? extends IFloodlightService>> services =
270 new ArrayList<Class<? extends IFloodlightService>>();
271 services.add(IOnosDeviceService.class);
272 return services;
273 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800274
TeruUd1c5b652014-03-24 13:58:46 -0700275 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
277 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
278 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
279 impls.put(IOnosDeviceService.class, this);
280 return impls;
281 }
282
TeruUd1c5b652014-03-24 13:58:46 -0700283 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700284 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
285 List<Class<? extends IFloodlightService>> dependencies =
286 new ArrayList<Class<? extends IFloodlightService>>();
287 dependencies.add(IFloodlightProviderService.class);
288 dependencies.add(INetworkGraphService.class);
289 dependencies.add(IDatagridService.class);
290 return dependencies;
291 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700292
Ray Milkey269ffb92014-04-03 14:43:30 -0700293 @Override
294 public void init(FloodlightModuleContext context)
295 throws FloodlightModuleException {
296 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700297 EXECUTOR_SERVICE.scheduleAtFixedRate(new CleanDevice(), 30, CLEANUP_SECOND, TimeUnit.SECONDS);
TeruU80ce5062014-03-03 17:16:13 -0800298
Ray Milkey269ffb92014-04-03 14:43:30 -0700299 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
300 datagrid = context.getServiceImpl(IDatagridService.class);
301 networkGraphService = context.getServiceImpl(INetworkGraphService.class);
302 networkGraph = networkGraphService.getNetworkGraph();
303 }
TeruU80ce5062014-03-03 17:16:13 -0800304
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 @Override
306 public void startUp(FloodlightModuleContext context) {
307 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
308 eventChannel = datagrid.addListener(DEVICE_CHANNEL_NAME, this,
309 Long.class,
310 OnosDevice.class);
311 }
TeruUd1c5b652014-03-24 13:58:46 -0700312
Ray Milkey269ffb92014-04-03 14:43:30 -0700313 @Override
314 public void deleteOnosDevice(OnosDevice dev) {
315 Long mac = dev.getMacAddress().toLong();
316 eventChannel.removeEntry(mac);
317 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
318 }
TeruUd1c5b652014-03-24 13:58:46 -0700319
Ray Milkey269ffb92014-04-03 14:43:30 -0700320 @Override
321 public void deleteOnosDeviceByMac(MACAddress mac) {
322 OnosDevice deleteDevice = mapDevice.get(mac);
323 deleteOnosDevice(deleteDevice);
324 }
325
326 @Override
327 public void addOnosDevice(Long mac, OnosDevice dev) {
328 eventChannel.addEntry(mac, dev);
329 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
330 }
331
332 @Override
333 public void entryAdded(OnosDevice dev) {
334 Long mac = dev.getMacAddress().toLong();
335 mapDevice.put(mac, dev);
336 log.debug("Device added: device mac {}", mac);
337 }
338
339 @Override
340 public void entryRemoved(OnosDevice dev) {
341 Long mac = dev.getMacAddress().toLong();
342 mapDevice.remove(mac);
343 log.debug("Device removed: device mac {}", mac);
344 }
345
346 @Override
347 public void entryUpdated(OnosDevice dev) {
348 Long mac = dev.getMacAddress().toLong();
349 mapDevice.put(mac, dev);
350 log.debug("Device updated: device mac {}", mac);
351 }
352
353 @Override
354 public void addOnosDeviceListener(IOnosDeviceListener listener) {
355 deviceListeners.add(listener);
356 }
357
358 @Override
359 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
360 deviceListeners.remove(listener);
361 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800362}