blob: 9f5fcdb605941eb80fe3af28f29a41731e1f0e92 [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 Hart472062d2014-04-03 10:56:48 -070030import net.onrc.onos.core.topology.INetworkGraphService;
31import net.onrc.onos.core.topology.NetworkGraph;
Jonathan Hart96892d12014-03-26 20:21:29 -070032import net.onrc.onos.packet.ARP;
33import net.onrc.onos.packet.DHCP;
34import net.onrc.onos.packet.Ethernet;
35import net.onrc.onos.packet.IPv4;
36import net.onrc.onos.packet.UDP;
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,
45 IOFMessageListener,
46 IOnosDeviceService,
47 IEventChannelListener<Long, OnosDevice> {
TeruU80ce5062014-03-03 17:16:13 -080048 protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
49 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
TeruUd1c5b652014-03-24 13:58:46 -070052 private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
Jonathan Hartd857ad62013-12-14 18:08:17 -080053 private IFloodlightProviderService floodlightProvider;
TeruU80ce5062014-03-03 17:16:13 -080054 private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070055
TeruU80ce5062014-03-03 17:16:13 -080056 private IDatagridService datagrid;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070057 private IEventChannel<Long, OnosDevice> eventChannel;
58 private static final String DEVICE_CHANNEL_NAME = "onos.device";
TeruU80ce5062014-03-03 17:16:13 -080059 private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
TeruUd1c5b652014-03-24 13:58:46 -070060 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
Jonathan Hart18ad9502013-12-15 18:28:00 -080067 private class OnosDeviceUpdate implements IUpdate {
Jonathan Hartd857ad62013-12-14 18:08:17 -080068 private OnosDevice device;
TeruU80ce5062014-03-03 17:16:13 -080069 private OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070070
TeruU80ce5062014-03-03 17:16:13 -080071 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
Jonathan Hartd857ad62013-12-14 18:08:17 -080072 this.device = device;
TeruU80ce5062014-03-03 17:16:13 -080073 this.type = type;
Jonathan Hartd857ad62013-12-14 18:08:17 -080074 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070075
Jonathan Hartd857ad62013-12-14 18:08:17 -080076 @Override
77 public void dispatch() {
TeruU80ce5062014-03-03 17:16:13 -080078 if(type == OnosDeviceUpdateType.ADD) {
TeruUd1c5b652014-03-24 13:58:46 -070079 for(IOnosDeviceListener listener: deviceListeners) {
80 listener.onosDeviceAdded(device);
81 }
TeruU80ce5062014-03-03 17:16:13 -080082 } else if (type == OnosDeviceUpdateType.DELETE){
TeruUd1c5b652014-03-24 13:58:46 -070083 for(IOnosDeviceListener listener: deviceListeners) {
84 listener.onosDeviceRemoved(device);
85 }
TeruU80ce5062014-03-03 17:16:13 -080086 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080087 }
88 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070089
Jonathan Hartd857ad62013-12-14 18:08:17 -080090 @Override
91 public String getName() {
92 return "onosdevicemanager";
93 }
94
95 @Override
96 public boolean isCallbackOrderingPrereq(OFType type, String name) {
Jonathan Hart18ad9502013-12-15 18:28:00 -080097 // 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);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800100 }
101
102 @Override
103 public boolean isCallbackOrderingPostreq(OFType type, String name) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700104 return type == OFType.PACKET_IN &&
Jonathan Hartd857ad62013-12-14 18:08:17 -0800105 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
106 }
107
108 @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
Jonathan Hartd857ad62013-12-14 18:08:17 -0800113 Ethernet eth = IFloodlightProviderService.bcStore.
114 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700115
Jonathan Hartd857ad62013-12-14 18:08:17 -0800116 return processPacketIn(sw, pi, eth);
117 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700118
Jonathan Hartd857ad62013-12-14 18:08:17 -0800119 return Command.CONTINUE;
120 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700121
Jonathan Hartd857ad62013-12-14 18:08:17 -0800122 private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU80ce5062014-03-03 17:16:13 -0800123 long dpid =sw.getId();
124 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
130 if (srcDevice == null){
131 return Command.STOP;
132 }
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
135 OnosDevice exDev = null;
136 if((exDev = mapDevice.get(mac)) != null ){
137 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;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700146 } else if (srcDevice.getIpv4Address() == null &&
147 exDev.getSwitchDPID().equals(srcDevice.getSwitchDPID()) &&
TeruUd1c5b652014-03-24 13:58:46 -0700148 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 }
TeruU80ce5062014-03-03 17:16:13 -0800160 }
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
TeruUd1c5b652014-03-24 13:58:46 -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
TeruU80ce5062014-03-03 17:16:13 -0800173 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());
176 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800177 return Command.CONTINUE;
178 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700179
180 //Thread to delete devices periodically.
TeruU80ce5062014-03-03 17:16:13 -0800181 //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
TeruU80ce5062014-03-03 17:16:13 -0800200 for(OnosDevice dev : deleteSet) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700201 deleteOnosDevice(dev);
TeruU80ce5062014-03-03 17:16:13 -0800202 }
203 } catch(Exception e) {
204 log.error("Error:", e);
205 }
206 }
207 }
208
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
212 * @param eth
213 * @param dlAddr
214 * @return
215 */
216 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
217 if (eth.getPayload() instanceof ARP) {
218 ARP arp = (ARP) eth.getPayload();
219 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
220 (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
221 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
222 }
223 } else if (eth.getPayload() instanceof IPv4) {
224 IPv4 ipv4 = (IPv4) eth.getPayload();
225 if (ipv4.getPayload() instanceof UDP) {
226 UDP udp = (UDP)ipv4.getPayload();
227 if (udp.getPayload() instanceof DHCP) {
228 DHCP dhcp = (DHCP)udp.getPayload();
229 if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
230 return ipv4.getSourceAddress();
231 }
232 }
233 }
234 }
235 return 0;
236 }
237
238 /**
239 * Parse an entity from an {@link Ethernet} packet.
240 * @param eth the packet to parse
241 * @param sw the switch on which the packet arrived
242 * @param pi the original packetin
243 * @return the entity from the packet
244 */
245 private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
246 long swdpid,
247 short port) {
248 byte[] dlAddrArr = eth.getSourceMACAddress();
249 long dlAddr = Ethernet.toLong(dlAddrArr);
250
251 // Ignore broadcast/multicast source
252 if ((dlAddrArr[0] & 0x1) != 0)
253 return null;
254
255 short vlan = eth.getVlanID();
256 int nwSrc = getSrcNwAddr(eth, dlAddr);
257 return new OnosDevice(MACAddress.valueOf(dlAddr),
258 ((vlan >= 0) ? vlan : null),
259 ((nwSrc != 0) ? nwSrc : null),
260 swdpid,
261 port,
262 new Date());
263 }
264
265 @Override
266 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700267 List<Class<? extends IFloodlightService>> services =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800268 new ArrayList<Class<? extends IFloodlightService>>();
269 services.add(IOnosDeviceService.class);
270 return services;
271 }
272
273 @Override
274 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700275 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800276 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
277 impls.put(IOnosDeviceService.class, this);
278 return impls;
279 }
280
281 @Override
282 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700283 List<Class<? extends IFloodlightService>> dependencies =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800284 new ArrayList<Class<? extends IFloodlightService>>();
285 dependencies.add(IFloodlightProviderService.class);
TeruUd1c5b652014-03-24 13:58:46 -0700286 dependencies.add(INetworkGraphService.class);
287 dependencies.add(IDatagridService.class);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800288 return dependencies;
289 }
290
291 @Override
292 public void init(FloodlightModuleContext context)
293 throws FloodlightModuleException {
294 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
TeruU80ce5062014-03-03 17:16:13 -0800295 executor.scheduleAtFixedRate(new CleanDevice(), 30 ,CLEANUP_SECOND, TimeUnit.SECONDS);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700296
TeruUd1c5b652014-03-24 13:58:46 -0700297 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
TeruU80ce5062014-03-03 17:16:13 -0800298 datagrid = context.getServiceImpl(IDatagridService.class);
TeruUd1c5b652014-03-24 13:58:46 -0700299 networkGraphService = context.getServiceImpl(INetworkGraphService.class);
300 networkGraph = networkGraphService.getNetworkGraph();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800301 }
302
303 @Override
304 public void startUp(FloodlightModuleContext context) {
305 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700306 eventChannel = datagrid.addListener(DEVICE_CHANNEL_NAME, this,
307 Long.class,
308 OnosDevice.class);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800309 }
310
TeruUd1c5b652014-03-24 13:58:46 -0700311 @Override
TeruU80ce5062014-03-03 17:16:13 -0800312 public void deleteOnosDevice(OnosDevice dev) {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700313 Long mac = dev.getMacAddress().toLong();
314 eventChannel.removeEntry(mac);
315 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
TeruU80ce5062014-03-03 17:16:13 -0800316 }
TeruUd1c5b652014-03-24 13:58:46 -0700317
318 @Override
319 public void deleteOnosDeviceByMac(MACAddress mac) {
320 OnosDevice deleteDevice = mapDevice.get(mac);
321 deleteOnosDevice(deleteDevice);
322 }
323
TeruU80ce5062014-03-03 17:16:13 -0800324 @Override
325 public void addOnosDevice(Long mac, OnosDevice dev) {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700326 eventChannel.addEntry(mac, dev);
327 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
TeruU80ce5062014-03-03 17:16:13 -0800328 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700329
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700330 @Override
331 public void entryAdded(OnosDevice dev) {
332 Long mac = dev.getMacAddress().toLong();
333 mapDevice.put(mac, dev);
334 log.debug("Device added: device mac {}", mac);
335 }
TeruU80ce5062014-03-03 17:16:13 -0800336
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700337 @Override
338 public void entryRemoved(OnosDevice dev) {
339 Long mac = dev.getMacAddress().toLong();
340 mapDevice.remove(mac);
341 log.debug("Device removed: device mac {}", mac);
342 }
TeruU80ce5062014-03-03 17:16:13 -0800343
Pavlin Radoslavov902fe522014-03-31 10:11:31 -0700344 @Override
345 public void entryUpdated(OnosDevice dev) {
346 Long mac = dev.getMacAddress().toLong();
347 mapDevice.put(mac, dev);
348 log.debug("Device updated: device mac {}", mac);
349 }
TeruUd1c5b652014-03-24 13:58:46 -0700350
351 @Override
352 public void addOnosDeviceListener(IOnosDeviceListener listener) {
353 deviceListeners.add(listener);
354 }
355
356 @Override
357 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
358 deviceListeners.remove(listener);
359 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800360}