blob: d6dffac3edb2bec91e2351e465940da2dddfee68 [file] [log] [blame]
Jonathan Hartd857ad62013-12-14 18:08:17 -08001package net.onrc.onos.ofcontroller.devicemanager;
2
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;
TeruU80ce5062014-03-03 17:16:13 -080027import net.onrc.onos.datagrid.IDatagridService;
Jonathan Hart96892d12014-03-26 20:21:29 -070028import net.onrc.onos.packet.ARP;
29import net.onrc.onos.packet.DHCP;
30import net.onrc.onos.packet.Ethernet;
31import net.onrc.onos.packet.IPv4;
32import net.onrc.onos.packet.UDP;
TeruUd1c5b652014-03-24 13:58:46 -070033import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
34import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
Jonathan Hartd857ad62013-12-14 18:08:17 -080035
36import org.openflow.protocol.OFMessage;
37import org.openflow.protocol.OFPacketIn;
38import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080041
42public class OnosDeviceManager implements IFloodlightModule, IOFMessageListener,
43 IOnosDeviceService {
TeruU80ce5062014-03-03 17:16:13 -080044 protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
45 private static final int CLEANUP_SECOND = 60*60;
46 private static final int AGEING_MILLSEC = 60*60*1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070047
TeruUd1c5b652014-03-24 13:58:46 -070048 private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
Jonathan Hartd857ad62013-12-14 18:08:17 -080049 private IFloodlightProviderService floodlightProvider;
TeruU80ce5062014-03-03 17:16:13 -080050 private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070051
TeruU80ce5062014-03-03 17:16:13 -080052 private IDatagridService datagrid;
53 private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
TeruUd1c5b652014-03-24 13:58:46 -070054 private INetworkGraphService networkGraphService;
55 private NetworkGraph networkGraph;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070056
TeruU80ce5062014-03-03 17:16:13 -080057 public enum OnosDeviceUpdateType {
58 ADD, DELETE, UPDATE;
59 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070060
Jonathan Hart18ad9502013-12-15 18:28:00 -080061 private class OnosDeviceUpdate implements IUpdate {
Jonathan Hartd857ad62013-12-14 18:08:17 -080062 private OnosDevice device;
TeruU80ce5062014-03-03 17:16:13 -080063 private OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070064
TeruU80ce5062014-03-03 17:16:13 -080065 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
Jonathan Hartd857ad62013-12-14 18:08:17 -080066 this.device = device;
TeruU80ce5062014-03-03 17:16:13 -080067 this.type = type;
Jonathan Hartd857ad62013-12-14 18:08:17 -080068 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070069
Jonathan Hartd857ad62013-12-14 18:08:17 -080070 @Override
71 public void dispatch() {
TeruU80ce5062014-03-03 17:16:13 -080072 if(type == OnosDeviceUpdateType.ADD) {
TeruUd1c5b652014-03-24 13:58:46 -070073 for(IOnosDeviceListener listener: deviceListeners) {
74 listener.onosDeviceAdded(device);
75 }
TeruU80ce5062014-03-03 17:16:13 -080076 } else if (type == OnosDeviceUpdateType.DELETE){
TeruUd1c5b652014-03-24 13:58:46 -070077 for(IOnosDeviceListener listener: deviceListeners) {
78 listener.onosDeviceRemoved(device);
79 }
TeruU80ce5062014-03-03 17:16:13 -080080 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080081 }
82 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070083
Jonathan Hartd857ad62013-12-14 18:08:17 -080084 @Override
85 public String getName() {
86 return "onosdevicemanager";
87 }
88
89 @Override
90 public boolean isCallbackOrderingPrereq(OFType type, String name) {
Jonathan Hart18ad9502013-12-15 18:28:00 -080091 // We want link discovery to consume LLDP first otherwise we'll
92 // end up reading bad device info from LLDP packets
93 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
Jonathan Hartd857ad62013-12-14 18:08:17 -080094 }
95
96 @Override
97 public boolean isCallbackOrderingPostreq(OFType type, String name) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070098 return type == OFType.PACKET_IN &&
Jonathan Hartd857ad62013-12-14 18:08:17 -080099 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
100 }
101
102 @Override
103 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
104 if (msg.getType().equals(OFType.PACKET_IN)) {
105 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700106
Jonathan Hartd857ad62013-12-14 18:08:17 -0800107 Ethernet eth = IFloodlightProviderService.bcStore.
108 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700109
Jonathan Hartd857ad62013-12-14 18:08:17 -0800110 return processPacketIn(sw, pi, eth);
111 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700112
Jonathan Hartd857ad62013-12-14 18:08:17 -0800113 return Command.CONTINUE;
114 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700115
Jonathan Hartd857ad62013-12-14 18:08:17 -0800116 private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU80ce5062014-03-03 17:16:13 -0800117 long dpid =sw.getId();
118 short portId = pi.getInPort();
119 Long mac = eth.getSourceMAC().toLong();
120
Jonathan Hartd857ad62013-12-14 18:08:17 -0800121 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800122 getSourceDeviceFromPacket(eth, dpid, portId);
123
124 if (srcDevice == null){
125 return Command.STOP;
126 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700127
TeruU80ce5062014-03-03 17:16:13 -0800128 //We check if it is the same device in datagrid to suppress the device update
129 OnosDevice exDev = null;
130 if((exDev = mapDevice.get(mac)) != null ){
131 if(exDev.equals(srcDevice)) {
132 //There is the same existing device. Update only ActiveSince time.
133 exDev.setLastSeenTimestamp(new Date());
134 if(log.isTraceEnabled()) {
135 log.debug("In the datagrid, there is the same device."
136 + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
137 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
138 }
139 return Command.CONTINUE;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700140 } else if (srcDevice.getIpv4Address() == null &&
141 exDev.getSwitchDPID().equals(srcDevice.getSwitchDPID()) &&
TeruUd1c5b652014-03-24 13:58:46 -0700142 exDev.getSwitchPort() == srcDevice.getSwitchPort()) {
143 //Vlan should be handled based on the Onos spec. Until then, don't handle it.
144 //Device attachment point and mac address are the same
145 //but the packet does not have an ip address.
146 exDev.setLastSeenTimestamp(new Date());
147 if(log.isTraceEnabled()) {
148 log.debug("In the datagrid, there is the same device with no ip."
149 + "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {} lastSeenTime {}",
150 dpid, portId, srcDevice.getMacAddress(), exDev.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
151 }
152 return Command.CONTINUE;
153 }
TeruU80ce5062014-03-03 17:16:13 -0800154 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700155
TeruU80ce5062014-03-03 17:16:13 -0800156 //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 -0700157 if(networkGraph.getLink(dpid, (long)portId) != null) {
158 if(log.isTraceEnabled()) {
159 log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
160 srcDevice.getMacAddress(), dpid, portId);
161 }
162 return Command.CONTINUE;
TeruU80ce5062014-03-03 17:16:13 -0800163 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700164
TeruU80ce5062014-03-03 17:16:13 -0800165 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700166
TeruU80ce5062014-03-03 17:16:13 -0800167 if(log.isTraceEnabled()) {
168 log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
169 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
170 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800171 return Command.CONTINUE;
172 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700173
174 //Thread to delete devices periodically.
TeruU80ce5062014-03-03 17:16:13 -0800175 //Remove all devices from the map first and then finally delete devices from the DB.
176 private class CleanDevice implements Runnable {
177 @Override
178 public void run() {
179 log.debug("called CleanDevice");
180 try{
181 Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
182 for (OnosDevice dev : mapDevice.values() ) {
183 long now = new Date().getTime();
184 if((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
185 if(log.isTraceEnabled()) {
186 log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
187 dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
188 dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
189 }
190 deleteSet.add(dev);
191 }
192 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700193
TeruU80ce5062014-03-03 17:16:13 -0800194 for(OnosDevice dev : deleteSet) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700195 deleteOnosDevice(dev);
TeruU80ce5062014-03-03 17:16:13 -0800196 }
197 } catch(Exception e) {
198 log.error("Error:", e);
199 }
200 }
201 }
202
Jonathan Hartd857ad62013-12-14 18:08:17 -0800203 /**
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700204 * Get IP address from packet if the packet is either an ARP
Jonathan Hartd857ad62013-12-14 18:08:17 -0800205 * or a DHCP packet
206 * @param eth
207 * @param dlAddr
208 * @return
209 */
210 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
211 if (eth.getPayload() instanceof ARP) {
212 ARP arp = (ARP) eth.getPayload();
213 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
214 (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
215 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
216 }
217 } else if (eth.getPayload() instanceof IPv4) {
218 IPv4 ipv4 = (IPv4) eth.getPayload();
219 if (ipv4.getPayload() instanceof UDP) {
220 UDP udp = (UDP)ipv4.getPayload();
221 if (udp.getPayload() instanceof DHCP) {
222 DHCP dhcp = (DHCP)udp.getPayload();
223 if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
224 return ipv4.getSourceAddress();
225 }
226 }
227 }
228 }
229 return 0;
230 }
231
232 /**
233 * Parse an entity from an {@link Ethernet} packet.
234 * @param eth the packet to parse
235 * @param sw the switch on which the packet arrived
236 * @param pi the original packetin
237 * @return the entity from the packet
238 */
239 private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
240 long swdpid,
241 short port) {
242 byte[] dlAddrArr = eth.getSourceMACAddress();
243 long dlAddr = Ethernet.toLong(dlAddrArr);
244
245 // Ignore broadcast/multicast source
246 if ((dlAddrArr[0] & 0x1) != 0)
247 return null;
248
249 short vlan = eth.getVlanID();
250 int nwSrc = getSrcNwAddr(eth, dlAddr);
251 return new OnosDevice(MACAddress.valueOf(dlAddr),
252 ((vlan >= 0) ? vlan : null),
253 ((nwSrc != 0) ? nwSrc : null),
254 swdpid,
255 port,
256 new Date());
257 }
258
259 @Override
260 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700261 List<Class<? extends IFloodlightService>> services =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800262 new ArrayList<Class<? extends IFloodlightService>>();
263 services.add(IOnosDeviceService.class);
264 return services;
265 }
266
267 @Override
268 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700269 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800270 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
271 impls.put(IOnosDeviceService.class, this);
272 return impls;
273 }
274
275 @Override
276 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700277 List<Class<? extends IFloodlightService>> dependencies =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800278 new ArrayList<Class<? extends IFloodlightService>>();
279 dependencies.add(IFloodlightProviderService.class);
TeruUd1c5b652014-03-24 13:58:46 -0700280 dependencies.add(INetworkGraphService.class);
281 dependencies.add(IDatagridService.class);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800282 return dependencies;
283 }
284
285 @Override
286 public void init(FloodlightModuleContext context)
287 throws FloodlightModuleException {
288 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
TeruU80ce5062014-03-03 17:16:13 -0800289 executor.scheduleAtFixedRate(new CleanDevice(), 30 ,CLEANUP_SECOND, TimeUnit.SECONDS);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700290
TeruUd1c5b652014-03-24 13:58:46 -0700291 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
TeruU80ce5062014-03-03 17:16:13 -0800292 datagrid = context.getServiceImpl(IDatagridService.class);
TeruUd1c5b652014-03-24 13:58:46 -0700293 networkGraphService = context.getServiceImpl(INetworkGraphService.class);
294 networkGraph = networkGraphService.getNetworkGraph();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800295 }
296
297 @Override
298 public void startUp(FloodlightModuleContext context) {
299 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
TeruU80ce5062014-03-03 17:16:13 -0800300 datagrid.registerMapDeviceEventHandler(new MapDevListener());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800301 }
302
TeruUd1c5b652014-03-24 13:58:46 -0700303 @Override
TeruU80ce5062014-03-03 17:16:13 -0800304 public void deleteOnosDevice(OnosDevice dev) {
305 datagrid.sendNotificationDeviceDeleted(dev);
306 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
307 }
TeruUd1c5b652014-03-24 13:58:46 -0700308
309 @Override
310 public void deleteOnosDeviceByMac(MACAddress mac) {
311 OnosDevice deleteDevice = mapDevice.get(mac);
312 deleteOnosDevice(deleteDevice);
313 }
314
TeruU80ce5062014-03-03 17:16:13 -0800315 @Override
316 public void addOnosDevice(Long mac, OnosDevice dev) {
317 datagrid.sendNotificationDeviceAdded(mac, dev);
318 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
319 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700320
TeruU80ce5062014-03-03 17:16:13 -0800321 //This is listener for datagrid mapDevice change.
322 class MapDevListener implements IDeviceEventHandler {
323
324 @Override
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700325 public void addDeviceEvent(Long mac, OnosDevice dev) {
TeruU80ce5062014-03-03 17:16:13 -0800326 mapDevice.put(mac, dev);
327 log.debug("addDeviceMap: device mac {}", mac);
328 }
329
330 @Override
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700331 public void deleteDeviceEvent(Long mac, OnosDevice dev) {
TeruU80ce5062014-03-03 17:16:13 -0800332 mapDevice.remove(mac);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700333 log.debug("deleteDeviceMap: device mac {}", mac);
TeruU80ce5062014-03-03 17:16:13 -0800334 }
335
336 @Override
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700337 public void updateDeviceEvent(Long mac, OnosDevice dev) {
TeruU80ce5062014-03-03 17:16:13 -0800338 mapDevice.put(mac, dev);
339 log.debug("updateDeviceMap: device mac {}", mac);
340 }
341 }
TeruUd1c5b652014-03-24 13:58:46 -0700342
343 @Override
344 public void addOnosDeviceListener(IOnosDeviceListener listener) {
345 deviceListeners.add(listener);
346 }
347
348 @Override
349 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
350 deviceListeners.remove(listener);
351 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800352}