blob: bb2e387e6325a1d5852fca3daa64f3a3827bcc3b [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;
12import java.util.concurrent.Executors;
13import java.util.concurrent.ScheduledExecutorService;
14import java.util.concurrent.TimeUnit;
Jonathan Hartd857ad62013-12-14 18:08:17 -080015
16import net.floodlightcontroller.core.FloodlightContext;
17import net.floodlightcontroller.core.IFloodlightProviderService;
18import net.floodlightcontroller.core.IOFMessageListener;
19import net.floodlightcontroller.core.IOFSwitch;
20import net.floodlightcontroller.core.IUpdate;
21import net.floodlightcontroller.core.module.FloodlightModuleContext;
22import net.floodlightcontroller.core.module.FloodlightModuleException;
23import net.floodlightcontroller.core.module.IFloodlightModule;
24import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hartd857ad62013-12-14 18:08:17 -080025import net.floodlightcontroller.util.MACAddress;
TeruU80ce5062014-03-03 17:16:13 -080026import net.onrc.onos.datagrid.IDatagridService;
Jonathan Hart96892d12014-03-26 20:21:29 -070027import net.onrc.onos.packet.ARP;
28import net.onrc.onos.packet.DHCP;
29import net.onrc.onos.packet.Ethernet;
30import net.onrc.onos.packet.IPv4;
31import net.onrc.onos.packet.UDP;
Jonathan Hartd857ad62013-12-14 18:08:17 -080032
33import org.openflow.protocol.OFMessage;
34import org.openflow.protocol.OFPacketIn;
35import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080038
39public class OnosDeviceManager implements IFloodlightModule, IOFMessageListener,
40 IOnosDeviceService {
TeruU80ce5062014-03-03 17:16:13 -080041 protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
42 private static final int CLEANUP_SECOND = 60*60;
43 private static final int AGEING_MILLSEC = 60*60*1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070044
Jonathan Hartd857ad62013-12-14 18:08:17 -080045 private IFloodlightProviderService floodlightProvider;
TeruU80ce5062014-03-03 17:16:13 -080046 private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070047
TeruU80ce5062014-03-03 17:16:13 -080048 private IDatagridService datagrid;
49 private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070050
TeruU80ce5062014-03-03 17:16:13 -080051 public enum OnosDeviceUpdateType {
52 ADD, DELETE, UPDATE;
53 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070054
Jonathan Hart18ad9502013-12-15 18:28:00 -080055 private class OnosDeviceUpdate implements IUpdate {
Jonathan Hartd857ad62013-12-14 18:08:17 -080056 private OnosDevice device;
TeruU80ce5062014-03-03 17:16:13 -080057 private OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070058
TeruU80ce5062014-03-03 17:16:13 -080059 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
Jonathan Hartd857ad62013-12-14 18:08:17 -080060 this.device = device;
TeruU80ce5062014-03-03 17:16:13 -080061 this.type = type;
Jonathan Hartd857ad62013-12-14 18:08:17 -080062 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070063
Jonathan Hartd857ad62013-12-14 18:08:17 -080064 @Override
65 public void dispatch() {
Pavlin Radoslavov5fe71882014-03-21 16:27:21 -070066 // TODO: Fix the code below after deviceStorage was removed
67 /*
TeruU80ce5062014-03-03 17:16:13 -080068 if(type == OnosDeviceUpdateType.ADD) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070069 deviceStorage.addOnosDevice(device);
TeruU80ce5062014-03-03 17:16:13 -080070 } else if (type == OnosDeviceUpdateType.DELETE){
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070071 deviceStorage.deleteOnosDevice(device);
TeruU80ce5062014-03-03 17:16:13 -080072 }
Pavlin Radoslavov5fe71882014-03-21 16:27:21 -070073 */
Jonathan Hartd857ad62013-12-14 18:08:17 -080074 }
75 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070076
Jonathan Hartd857ad62013-12-14 18:08:17 -080077 @Override
78 public String getName() {
79 return "onosdevicemanager";
80 }
81
82 @Override
83 public boolean isCallbackOrderingPrereq(OFType type, String name) {
Jonathan Hart18ad9502013-12-15 18:28:00 -080084 // We want link discovery to consume LLDP first otherwise we'll
85 // end up reading bad device info from LLDP packets
86 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
Jonathan Hartd857ad62013-12-14 18:08:17 -080087 }
88
89 @Override
90 public boolean isCallbackOrderingPostreq(OFType type, String name) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070091 return type == OFType.PACKET_IN &&
Jonathan Hartd857ad62013-12-14 18:08:17 -080092 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
93 }
94
95 @Override
96 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
97 if (msg.getType().equals(OFType.PACKET_IN)) {
98 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070099
Jonathan Hartd857ad62013-12-14 18:08:17 -0800100 Ethernet eth = IFloodlightProviderService.bcStore.
101 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700102
Jonathan Hartd857ad62013-12-14 18:08:17 -0800103 return processPacketIn(sw, pi, eth);
104 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700105
Jonathan Hartd857ad62013-12-14 18:08:17 -0800106 return Command.CONTINUE;
107 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700108
Jonathan Hartd857ad62013-12-14 18:08:17 -0800109 private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU80ce5062014-03-03 17:16:13 -0800110 long dpid =sw.getId();
111 short portId = pi.getInPort();
112 Long mac = eth.getSourceMAC().toLong();
113
Jonathan Hartd857ad62013-12-14 18:08:17 -0800114 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800115 getSourceDeviceFromPacket(eth, dpid, portId);
116
117 if (srcDevice == null){
118 return Command.STOP;
119 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700120
TeruU80ce5062014-03-03 17:16:13 -0800121 //We check if it is the same device in datagrid to suppress the device update
122 OnosDevice exDev = null;
123 if((exDev = mapDevice.get(mac)) != null ){
124 if(exDev.equals(srcDevice)) {
125 //There is the same existing device. Update only ActiveSince time.
126 exDev.setLastSeenTimestamp(new Date());
127 if(log.isTraceEnabled()) {
128 log.debug("In the datagrid, there is the same device."
129 + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
130 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
131 }
132 return Command.CONTINUE;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700133 } else if (srcDevice.getIpv4Address() == null &&
134 exDev.getSwitchDPID().equals(srcDevice.getSwitchDPID()) &&
TeruU80ce5062014-03-03 17:16:13 -0800135 exDev.getSwitchPort() == srcDevice.getSwitchPort() &&
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700136 exDev.getVlan().equals(srcDevice.getVlan())) {
137 //Device attachment point and mac address are the same
TeruU80ce5062014-03-03 17:16:13 -0800138 //but the packet does not have an ip address.
139 exDev.setLastSeenTimestamp(new Date());
140 if(log.isTraceEnabled()) {
141 log.debug("In the datagrid, there is the same device with no ip."
142 + "Keep ip and 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 }
147 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700148
TeruU80ce5062014-03-03 17:16:13 -0800149 //If the switch port we try to attach a new device already has a link, then stop adding device
Pavlin Radoslavov1237e392014-03-20 16:24:06 -0700150 // TODO: Fix/update this after we refactor the Device Discovery mechanism
151 /*
TeruU80ce5062014-03-03 17:16:13 -0800152 Collection<TopologyElement> list = datagrid.getAllTopologyElements();
153 for(TopologyElement elem: list) {
154 if(elem.getType() == Type.ELEMENT_LINK) {
155 if((elem.getFromPort() == portId && elem.getFromSwitch() == dpid) ||
156 (elem.getToPort() == portId && elem.getToSwitch() == dpid)) {
157 if(log.isTraceEnabled()) {
158 log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
159 srcDevice.getMacAddress(), dpid, portId);
160 }
161 return Command.CONTINUE;
162 }
163 }
164 }
Pavlin Radoslavov1237e392014-03-20 16:24:06 -0700165 */
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700166
TeruU80ce5062014-03-03 17:16:13 -0800167 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700168
TeruU80ce5062014-03-03 17:16:13 -0800169 if(log.isTraceEnabled()) {
170 log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
171 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
172 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800173 return Command.CONTINUE;
174 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700175
176 //Thread to delete devices periodically.
TeruU80ce5062014-03-03 17:16:13 -0800177 //Remove all devices from the map first and then finally delete devices from the DB.
178 private class CleanDevice implements Runnable {
179 @Override
180 public void run() {
181 log.debug("called CleanDevice");
182 try{
183 Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
184 for (OnosDevice dev : mapDevice.values() ) {
185 long now = new Date().getTime();
186 if((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
187 if(log.isTraceEnabled()) {
188 log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
189 dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
190 dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
191 }
192 deleteSet.add(dev);
193 }
194 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700195
TeruU80ce5062014-03-03 17:16:13 -0800196 for(OnosDevice dev : deleteSet) {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700197 deleteOnosDevice(dev);
TeruU80ce5062014-03-03 17:16:13 -0800198 }
199 } catch(Exception e) {
200 log.error("Error:", e);
201 }
202 }
203 }
204
Jonathan Hartd857ad62013-12-14 18:08:17 -0800205 /**
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700206 * Get IP address from packet if the packet is either an ARP
Jonathan Hartd857ad62013-12-14 18:08:17 -0800207 * or a DHCP packet
208 * @param eth
209 * @param dlAddr
210 * @return
211 */
212 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
213 if (eth.getPayload() instanceof ARP) {
214 ARP arp = (ARP) eth.getPayload();
215 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
216 (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
217 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
218 }
219 } else if (eth.getPayload() instanceof IPv4) {
220 IPv4 ipv4 = (IPv4) eth.getPayload();
221 if (ipv4.getPayload() instanceof UDP) {
222 UDP udp = (UDP)ipv4.getPayload();
223 if (udp.getPayload() instanceof DHCP) {
224 DHCP dhcp = (DHCP)udp.getPayload();
225 if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
226 return ipv4.getSourceAddress();
227 }
228 }
229 }
230 }
231 return 0;
232 }
233
234 /**
235 * Parse an entity from an {@link Ethernet} packet.
236 * @param eth the packet to parse
237 * @param sw the switch on which the packet arrived
238 * @param pi the original packetin
239 * @return the entity from the packet
240 */
241 private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
242 long swdpid,
243 short port) {
244 byte[] dlAddrArr = eth.getSourceMACAddress();
245 long dlAddr = Ethernet.toLong(dlAddrArr);
246
247 // Ignore broadcast/multicast source
248 if ((dlAddrArr[0] & 0x1) != 0)
249 return null;
250
251 short vlan = eth.getVlanID();
252 int nwSrc = getSrcNwAddr(eth, dlAddr);
253 return new OnosDevice(MACAddress.valueOf(dlAddr),
254 ((vlan >= 0) ? vlan : null),
255 ((nwSrc != 0) ? nwSrc : null),
256 swdpid,
257 port,
258 new Date());
259 }
260
261 @Override
262 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700263 List<Class<? extends IFloodlightService>> services =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800264 new ArrayList<Class<? extends IFloodlightService>>();
265 services.add(IOnosDeviceService.class);
266 return services;
267 }
268
269 @Override
270 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700271 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800272 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
273 impls.put(IOnosDeviceService.class, this);
274 return impls;
275 }
276
277 @Override
278 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700279 List<Class<? extends IFloodlightService>> dependencies =
Jonathan Hartd857ad62013-12-14 18:08:17 -0800280 new ArrayList<Class<? extends IFloodlightService>>();
281 dependencies.add(IFloodlightProviderService.class);
282 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
TeruU80ce5062014-03-03 17:16:13 -0800291 datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800292 }
293
294 @Override
295 public void startUp(FloodlightModuleContext context) {
296 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
TeruU80ce5062014-03-03 17:16:13 -0800297 datagrid.registerMapDeviceEventHandler(new MapDevListener());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800298 }
299
TeruU80ce5062014-03-03 17:16:13 -0800300 @Override
301 public void deleteOnosDevice(OnosDevice dev) {
302 datagrid.sendNotificationDeviceDeleted(dev);
303 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
304 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700305
TeruU80ce5062014-03-03 17:16:13 -0800306 @Override
307 public void addOnosDevice(Long mac, OnosDevice dev) {
308 datagrid.sendNotificationDeviceAdded(mac, dev);
309 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
310 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700311
TeruU80ce5062014-03-03 17:16:13 -0800312 //This is listener for datagrid mapDevice change.
313 class MapDevListener implements IDeviceEventHandler {
314
315 @Override
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700316 public void addDeviceEvent(Long mac, OnosDevice dev) {
TeruU80ce5062014-03-03 17:16:13 -0800317 mapDevice.put(mac, dev);
318 log.debug("addDeviceMap: device mac {}", mac);
319 }
320
321 @Override
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700322 public void deleteDeviceEvent(Long mac, OnosDevice dev) {
TeruU80ce5062014-03-03 17:16:13 -0800323 mapDevice.remove(mac);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700324 log.debug("deleteDeviceMap: device mac {}", mac);
TeruU80ce5062014-03-03 17:16:13 -0800325 }
326
327 @Override
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700328 public void updateDeviceEvent(Long mac, OnosDevice dev) {
TeruU80ce5062014-03-03 17:16:13 -0800329 mapDevice.put(mac, dev);
330 log.debug("updateDeviceMap: device mac {}", mac);
331 }
332 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800333}