// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.agent.dhcp; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.jnetpcap.Pcap; import org.jnetpcap.PcapBpfProgram; import org.jnetpcap.PcapIf; import org.jnetpcap.packet.PcapPacket; import org.jnetpcap.packet.PcapPacketHandler; import org.jnetpcap.protocol.tcpip.Udp; import com.cloud.utils.Pair; import com.cloud.utils.concurrency.NamedThreadFactory; @Local(value = { DhcpSnooper.class }) public class DhcpSnooperImpl implements DhcpSnooper { private static final Logger s_logger = Logger .getLogger(DhcpSnooperImpl.class); public enum DHCPState { DHCPACKED, DHCPREQUESTED, DHCPRESET; } public class IPAddr { String _vmName; InetAddress _ip; DHCPState _state; public IPAddr(InetAddress ip, DHCPState state, String vmName) { _ip = ip; _state = state; _vmName = vmName; } } protected ExecutorService _executor; protected Map<String, IPAddr> _macIpMap; protected Map<InetAddress, String> _ipMacMap; private DhcpServer _server; protected long _timeout = 1200000; protected InetAddress _dhcpServerIp; public DhcpSnooperImpl(String bridge, long timeout) { _timeout = timeout; _executor = new ThreadPoolExecutor(10, 10 * 10, 1, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory( "DhcpListener")); _macIpMap = new ConcurrentHashMap<String, IPAddr>(); _ipMacMap = new ConcurrentHashMap<InetAddress, String>(); _server = new DhcpServer(this, bridge); _server.start(); } @Override public InetAddress getIPAddr(String macAddr, String vmName) { String macAddrLowerCase = macAddr.toLowerCase(); IPAddr addr = _macIpMap.get(macAddrLowerCase); if (addr == null) { addr = new IPAddr(null, DHCPState.DHCPRESET, vmName); _macIpMap.put(macAddrLowerCase, addr); } else { addr._state = DHCPState.DHCPRESET; } synchronized (addr) { try { addr.wait(_timeout); } catch (InterruptedException e) { } if (addr._state == DHCPState.DHCPACKED) { addr._state = DHCPState.DHCPRESET; return addr._ip; } } return null; } public InetAddress getDhcpServerIP() { return _dhcpServerIp; } @Override public void cleanup(String macAddr, String vmName) { try { if (macAddr == null) { return; } _macIpMap.remove(macAddr); _ipMacMap.values().remove(macAddr); } catch (Exception e) { s_logger.debug("Failed to cleanup: " + e.toString()); } } @Override public Map<String, InetAddress> syncIpAddr() { Collection<IPAddr> ips = _macIpMap.values(); HashMap<String, InetAddress> vmIpMap = new HashMap<String, InetAddress>(); for (IPAddr ip : ips) { if (ip._state == DHCPState.DHCPACKED) { vmIpMap.put(ip._vmName, ip._ip); } } return vmIpMap; } @Override public void initializeMacTable(List<Pair<String, String>> macVmNameList) { for (Pair<String, String> macVmname : macVmNameList) { IPAddr ipAdrr = new IPAddr(null, DHCPState.DHCPRESET, macVmname.second()); _macIpMap.put(macVmname.first(), ipAdrr); } } protected void setIPAddr(String macAddr, InetAddress ip, DHCPState state, InetAddress dhcpServerIp) { String macAddrLowerCase = macAddr.toLowerCase(); if (state == DHCPState.DHCPREQUESTED) { IPAddr ipAddr = _macIpMap.get(macAddrLowerCase); if (ipAddr == null) { return; } _ipMacMap.put(ip, macAddr); } else if (state == DHCPState.DHCPACKED) { _dhcpServerIp = dhcpServerIp; String destMac = macAddrLowerCase; if (macAddrLowerCase.equalsIgnoreCase("ff:ff:ff:ff:ff:ff")) { destMac = _ipMacMap.get(ip); if (destMac == null) { return; } } IPAddr addr = _macIpMap.get(destMac); if (addr != null) { addr._ip = ip; addr._state = state; synchronized (addr) { addr.notify(); } } } } /* * (non-Javadoc) * * @see com.cloud.agent.dhcp.DhcpSnooper#stop() */ @Override public boolean stop() { _executor.shutdown(); _server.StopServer(); return true; } private class DhcpServer extends Thread { private DhcpSnooperImpl _manager; private String _bridge; private Pcap _pcapedDev; private boolean _loop; public DhcpServer(DhcpSnooperImpl mgt, String bridge) { _manager = mgt; _bridge = bridge; _loop = true; } public void StopServer() { _loop = false; _pcapedDev.breakloop(); _pcapedDev.close(); } private Pcap initializePcap() { try { List<PcapIf> alldevs = new ArrayList<PcapIf>(); StringBuilder errBuf = new StringBuilder(); int r = Pcap.findAllDevs(alldevs, errBuf); if (r == Pcap.NOT_OK || alldevs.isEmpty()) { return null; } PcapIf dev = null; for (PcapIf device : alldevs) { if (device.getName().equalsIgnoreCase(_bridge)) { dev = device; break; } } if (dev == null) { s_logger.debug("Pcap: Can't find device: " + _bridge + " to listen on"); return null; } int snaplen = 64 * 1024; int flags = Pcap.MODE_PROMISCUOUS; int timeout = 10 * 1000; Pcap pcap = Pcap.openLive(dev.getName(), snaplen, flags, timeout, errBuf); if (pcap == null) { s_logger.debug("Pcap: Can't open " + _bridge); return null; } PcapBpfProgram program = new PcapBpfProgram(); String expr = "dst port 68 or 67"; int optimize = 0; int netmask = 0xFFFFFF00; if (pcap.compile(program, expr, optimize, netmask) != Pcap.OK) { s_logger.debug("Pcap: can't compile BPF"); return null; } if (pcap.setFilter(program) != Pcap.OK) { s_logger.debug("Pcap: Can't set filter"); return null; } return pcap; } catch (Exception e) { s_logger.debug("Failed to initialized: " + e.toString()); } return null; } public void run() { while (_loop) { try { _pcapedDev = initializePcap(); if (_pcapedDev == null) { return; } PcapPacketHandler<String> jpacketHandler = new PcapPacketHandler<String>() { public void nextPacket(PcapPacket packet, String user) { Udp u = new Udp(); if (packet.hasHeader(u)) { int offset = u.getOffset() + u.getLength(); _executor.execute(new DhcpPacketParser(packet, offset, u.length() - u.getLength(), _manager)); } } }; s_logger.debug("Starting DHCP snooping on " + _bridge); int retValue = _pcapedDev.loop(-1, jpacketHandler, "pcapPacketHandler"); if (retValue == -1) { s_logger.debug("Pcap: failed to set loop handler"); } else if (retValue == -2 && !_loop) { s_logger.debug("Pcap: terminated"); return; } _pcapedDev.close(); } catch (Exception e) { s_logger.debug("Pcap error:" + e.toString()); } } } } static public void main(String args[]) { s_logger.addAppender(new org.apache.log4j.ConsoleAppender( new org.apache.log4j.PatternLayout(), "System.out")); final DhcpSnooperImpl manager = new DhcpSnooperImpl("cloudbr0", 10000); s_logger.debug(manager.getIPAddr("02:00:4c:66:00:03", "i-2-5-VM")); manager.stop(); } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { // TODO configure timeout here return true; } @Override public boolean start() { return true; } @Override public String getName() { return "DhcpSnooperImpl"; } }