blob: e9ddfa76860954686373afa9e873f0fcf0046d06 [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;
25import net.floodlightcontroller.packet.ARP;
26import net.floodlightcontroller.packet.DHCP;
27import net.floodlightcontroller.packet.Ethernet;
28import net.floodlightcontroller.packet.IPv4;
29import net.floodlightcontroller.packet.UDP;
30import net.floodlightcontroller.util.MACAddress;
TeruU80ce5062014-03-03 17:16:13 -080031import net.onrc.onos.datagrid.IDatagridService;
Jonathan Hartd857ad62013-12-14 18:08:17 -080032import net.onrc.onos.ofcontroller.core.IDeviceStorage;
33import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
TeruU80ce5062014-03-03 17:16:13 -080034import net.onrc.onos.ofcontroller.topology.TopologyElement;
35import net.onrc.onos.ofcontroller.topology.TopologyElement.Type;
Jonathan Hartd857ad62013-12-14 18:08:17 -080036
37import org.openflow.protocol.OFMessage;
38import org.openflow.protocol.OFPacketIn;
39import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080042
43public class OnosDeviceManager implements IFloodlightModule, IOFMessageListener,
44 IOnosDeviceService {
TeruU80ce5062014-03-03 17:16:13 -080045 protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
46 private static final int CLEANUP_SECOND = 60*60;
47 private static final int AGEING_MILLSEC = 60*60*1000;
Jonathan Hartd857ad62013-12-14 18:08:17 -080048
TeruU80ce5062014-03-03 17:16:13 -080049 private IDeviceStorage deviceStorage;
Jonathan Hartd857ad62013-12-14 18:08:17 -080050 private IFloodlightProviderService floodlightProvider;
TeruU80ce5062014-03-03 17:16:13 -080051 private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
52
53 private IDatagridService datagrid;
54 private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
55
56 public enum OnosDeviceUpdateType {
57 ADD, DELETE, UPDATE;
58 }
59
Jonathan Hart18ad9502013-12-15 18:28:00 -080060 private class OnosDeviceUpdate implements IUpdate {
Jonathan Hartd857ad62013-12-14 18:08:17 -080061 private OnosDevice device;
TeruU80ce5062014-03-03 17:16:13 -080062 private OnosDeviceUpdateType type;
Jonathan Hartd857ad62013-12-14 18:08:17 -080063
TeruU80ce5062014-03-03 17:16:13 -080064 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
Jonathan Hartd857ad62013-12-14 18:08:17 -080065 this.device = device;
TeruU80ce5062014-03-03 17:16:13 -080066 this.type = type;
Jonathan Hartd857ad62013-12-14 18:08:17 -080067 }
68
69 @Override
70 public void dispatch() {
TeruU80ce5062014-03-03 17:16:13 -080071 if(type == OnosDeviceUpdateType.ADD) {
72 deviceStorage.addOnosDevice(device);
73 } else if (type == OnosDeviceUpdateType.DELETE){
74 deviceStorage.deleteOnosDevice(device);
75 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080076 }
77 }
78
79 @Override
80 public String getName() {
81 return "onosdevicemanager";
82 }
83
84 @Override
85 public boolean isCallbackOrderingPrereq(OFType type, String name) {
Jonathan Hart18ad9502013-12-15 18:28:00 -080086 // We want link discovery to consume LLDP first otherwise we'll
87 // end up reading bad device info from LLDP packets
88 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
Jonathan Hartd857ad62013-12-14 18:08:17 -080089 }
90
91 @Override
92 public boolean isCallbackOrderingPostreq(OFType type, String name) {
93 return type == OFType.PACKET_IN &&
94 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
95 }
96
97 @Override
98 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
99 if (msg.getType().equals(OFType.PACKET_IN)) {
100 OFPacketIn pi = (OFPacketIn) msg;
101
102 Ethernet eth = IFloodlightProviderService.bcStore.
103 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
104
105 return processPacketIn(sw, pi, eth);
106 }
107
108 return Command.CONTINUE;
109 }
110
111 private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU80ce5062014-03-03 17:16:13 -0800112 long dpid =sw.getId();
113 short portId = pi.getInPort();
114 Long mac = eth.getSourceMAC().toLong();
115
Jonathan Hartd857ad62013-12-14 18:08:17 -0800116 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800117 getSourceDeviceFromPacket(eth, dpid, portId);
118
119 if (srcDevice == null){
120 return Command.STOP;
121 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800122
TeruU80ce5062014-03-03 17:16:13 -0800123 //We check if it is the same device in datagrid to suppress the device update
124 OnosDevice exDev = null;
125 if((exDev = mapDevice.get(mac)) != null ){
126 if(exDev.equals(srcDevice)) {
127 //There is the same existing device. Update only ActiveSince time.
128 exDev.setLastSeenTimestamp(new Date());
129 if(log.isTraceEnabled()) {
130 log.debug("In the datagrid, there is the same device."
131 + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
132 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
133 }
134 return Command.CONTINUE;
135 } else if (srcDevice.getIpv4Address() == null &&
136 exDev.getSwitchDPID() == srcDevice.getSwitchDPID() &&
137 exDev.getSwitchPort() == srcDevice.getSwitchPort() &&
138 exDev.getVlan() == srcDevice.getVlan()) {
139 //Device attachment point and mac address are the same
140 //but the packet does not have an ip address.
141 exDev.setLastSeenTimestamp(new Date());
142 if(log.isTraceEnabled()) {
143 log.debug("In the datagrid, there is the same device with no ip."
144 + "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
145 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
146 }
147 return Command.CONTINUE;
148 }
149 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800150
TeruU80ce5062014-03-03 17:16:13 -0800151 //If the switch port we try to attach a new device already has a link, then stop adding device
152 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 }
165
166 addOnosDevice(mac, srcDevice);
167
168 if(log.isTraceEnabled()) {
169 log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
170 dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
171 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800172 return Command.CONTINUE;
173 }
174
TeruU80ce5062014-03-03 17:16:13 -0800175 //Thread to delete devices periodically.
176 //Remove all devices from the map first and then finally delete devices from the DB.
177 private class CleanDevice implements Runnable {
178 @Override
179 public void run() {
180 log.debug("called CleanDevice");
181 try{
182 Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
183 for (OnosDevice dev : mapDevice.values() ) {
184 long now = new Date().getTime();
185 if((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
186 if(log.isTraceEnabled()) {
187 log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
188 dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
189 dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
190 }
191 deleteSet.add(dev);
192 }
193 }
194
195 for(OnosDevice dev : deleteSet) {
196 deleteOnosDevice(dev);
197 }
198 } catch(Exception e) {
199 log.error("Error:", e);
200 }
201 }
202 }
203
Jonathan Hartd857ad62013-12-14 18:08:17 -0800204 /**
205 * Get IP address from packet if the packet is either an ARP
206 * or a DHCP packet
207 * @param eth
208 * @param dlAddr
209 * @return
210 */
211 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
212 if (eth.getPayload() instanceof ARP) {
213 ARP arp = (ARP) eth.getPayload();
214 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
215 (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
216 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
217 }
218 } else if (eth.getPayload() instanceof IPv4) {
219 IPv4 ipv4 = (IPv4) eth.getPayload();
220 if (ipv4.getPayload() instanceof UDP) {
221 UDP udp = (UDP)ipv4.getPayload();
222 if (udp.getPayload() instanceof DHCP) {
223 DHCP dhcp = (DHCP)udp.getPayload();
224 if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
225 return ipv4.getSourceAddress();
226 }
227 }
228 }
229 }
230 return 0;
231 }
232
233 /**
234 * Parse an entity from an {@link Ethernet} packet.
235 * @param eth the packet to parse
236 * @param sw the switch on which the packet arrived
237 * @param pi the original packetin
238 * @return the entity from the packet
239 */
240 private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
241 long swdpid,
242 short port) {
243 byte[] dlAddrArr = eth.getSourceMACAddress();
244 long dlAddr = Ethernet.toLong(dlAddrArr);
245
246 // Ignore broadcast/multicast source
247 if ((dlAddrArr[0] & 0x1) != 0)
248 return null;
249
250 short vlan = eth.getVlanID();
251 int nwSrc = getSrcNwAddr(eth, dlAddr);
252 return new OnosDevice(MACAddress.valueOf(dlAddr),
253 ((vlan >= 0) ? vlan : null),
254 ((nwSrc != 0) ? nwSrc : null),
255 swdpid,
256 port,
257 new Date());
258 }
259
260 @Override
261 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
262 List<Class<? extends IFloodlightService>> services =
263 new ArrayList<Class<? extends IFloodlightService>>();
264 services.add(IOnosDeviceService.class);
265 return services;
266 }
267
268 @Override
269 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
270 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
271 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
272 impls.put(IOnosDeviceService.class, this);
273 return impls;
274 }
275
276 @Override
277 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
278 List<Class<? extends IFloodlightService>> dependencies =
279 new ArrayList<Class<? extends IFloodlightService>>();
280 dependencies.add(IFloodlightProviderService.class);
281 return dependencies;
282 }
283
284 @Override
285 public void init(FloodlightModuleContext context)
286 throws FloodlightModuleException {
287 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
TeruU80ce5062014-03-03 17:16:13 -0800288 executor.scheduleAtFixedRate(new CleanDevice(), 30 ,CLEANUP_SECOND, TimeUnit.SECONDS);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800289 deviceStorage = new DeviceStorageImpl();
Yuta HIGUCHI0c20db82013-12-17 23:06:36 -0800290 deviceStorage.init("","");
TeruU80ce5062014-03-03 17:16:13 -0800291
292 datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Hartd857ad62013-12-14 18:08:17 -0800293 }
294
295 @Override
296 public void startUp(FloodlightModuleContext context) {
297 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
TeruU80ce5062014-03-03 17:16:13 -0800298 datagrid.registerMapDeviceEventHandler(new MapDevListener());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800299 }
300
TeruU80ce5062014-03-03 17:16:13 -0800301 @Override
302 public void deleteOnosDevice(OnosDevice dev) {
303 datagrid.sendNotificationDeviceDeleted(dev);
304 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
305 }
306
307 @Override
308 public void addOnosDevice(Long mac, OnosDevice dev) {
309 datagrid.sendNotificationDeviceAdded(mac, dev);
310 floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
311 }
312
313 //This is listener for datagrid mapDevice change.
314 class MapDevListener implements IDeviceEventHandler {
315
316 @Override
317 public void addDeviceEvent(Long mac, OnosDevice dev) {
318 mapDevice.put(mac, dev);
319 log.debug("addDeviceMap: device mac {}", mac);
320 }
321
322 @Override
323 public void deleteDeviceEvent(Long mac, OnosDevice dev) {
324 mapDevice.remove(mac);
325 log.debug("deleteDeviceMap: device mac {}", mac);
326 }
327
328 @Override
329 public void updateDeviceEvent(Long mac, OnosDevice dev) {
330 mapDevice.put(mac, dev);
331 log.debug("updateDeviceMap: device mac {}", mac);
332 }
333 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800334}