/*
* Copyright 2011 Future Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.
*/
package org.krakenapps.ipmanager.impl;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.Validate;
import org.krakenapps.ipmanager.IpDetection;
import org.krakenapps.ipmanager.IpManager;
import org.krakenapps.ipmanager.IpMonitor;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.dhcp.DhcpDecoder;
import org.krakenapps.pcap.decoder.dhcp.DhcpMessage;
import org.krakenapps.pcap.decoder.dhcp.DhcpMessage.Type;
import org.krakenapps.pcap.decoder.dhcp.DhcpOptions;
import org.krakenapps.pcap.decoder.dhcp.DhcpProcessor;
import org.krakenapps.pcap.decoder.dhcp.fingerprint.FingerprintDetector;
import org.krakenapps.pcap.decoder.dhcp.fingerprint.FingerprintMetadata;
import org.krakenapps.pcap.decoder.ethernet.EthernetFrame;
import org.krakenapps.pcap.decoder.ethernet.MacAddress;
import org.krakenapps.pcap.decoder.ip.IpPacket;
import org.krakenapps.pcap.decoder.netbios.NetBiosDecoder;
import org.krakenapps.pcap.decoder.netbios.NetBiosNamePacket;
import org.krakenapps.pcap.decoder.netbios.NetBiosNameProcessor;
import org.krakenapps.pcap.decoder.netbios.NetBiosNameHeader.Opcode;
import org.krakenapps.pcap.decoder.netbios.rr.NbResourceRecord;
import org.krakenapps.pcap.decoder.udp.UdpPacket;
import org.krakenapps.pcap.decoder.udp.UdpProcessor;
import org.krakenapps.pcap.live.PcapDevice;
import org.krakenapps.pcap.live.PcapDeviceManager;
import org.krakenapps.pcap.live.PcapDeviceMetadata;
import org.krakenapps.pcap.live.PcapStreamEventListener;
import org.krakenapps.pcap.util.PcapLiveRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Monitor IP entries of local area network
*/
@Component(name = "ipm-ip-monitor")
@Provides(specifications = { IpMonitor.class })
public class IpMonitorService implements IpMonitor, PcapStreamEventListener, UdpProcessor, NetBiosNameProcessor,
DhcpProcessor {
private final Logger logger = LoggerFactory.getLogger(IpMonitorService.class.getName());
@Requires
private IpManager ipManager;
private Map<PcapLiveRunner, Thread> stream = new HashMap<PcapLiveRunner, Thread>();
private NetBiosDecoder netbios;
private DhcpDecoder dhcp;
private Map<Integer, TemporaryIpDetection> dhcpRequests = new HashMap<Integer, TemporaryIpDetection>();
private PriorityQueue<RequestTimestamp> reqTimestamps = new PriorityQueue<RequestTimestamp>(11,
new RequestTimestampComparator());
@Validate
public void start() {
netbios = new NetBiosDecoder();
netbios.registerNameProcessor(this);
dhcp = new DhcpDecoder();
dhcp.register(this);
for (PcapDeviceMetadata metadata : PcapDeviceManager.getDeviceMetadataList()) {
try {
PcapDevice device = PcapDeviceManager.open(metadata.getName(), 30000);
device.setFilter("udp");
PcapLiveRunner runner = new PcapLiveRunner(device);
runner.getUdpDecoder().registerUdpProcessor(this);
runner.setUdpProcessor(Protocol.DHCP, dhcp);
runner.setUdpProcessor(Protocol.NETBIOS, netbios);
Thread thread = new Thread(runner, "IP Monitor " + device.getMetadata().getName());
stream.put(runner, thread);
thread.start();
} catch (IOException e) {
logger.error("kraken ipmanager: device open failed (" + metadata.getName() + ")", e);
}
}
for (PcapLiveRunner runner : stream.keySet())
onOpen("ipm", runner);
}
@Invalidate
public void stop() {
// remove all callbacks
if (stream != null) {
for (PcapLiveRunner runner : stream.keySet()) {
stream.get(runner).interrupt();
onClose("ipm", runner);
try {
runner.getDevice().close();
} catch (IOException e) {
logger.error("kraken ipmanager: device close failed (" + runner.getDevice().getMetadata().getName()
+ ")", e);
}
}
stream.clear();
}
dhcp.unregister(this);
netbios.unregisterNameProcessor(this);
}
@Override
public void onOpen(String key, PcapLiveRunner runner) {
runner.setUdpProcessor(Protocol.NETBIOS, netbios);
runner.setUdpProcessor(Protocol.DHCP, dhcp);
runner.getUdpDecoder().registerUdpProcessor(this);
logger.info("kraken ipmanager: connected pcap stream [{}], device [{}]", key, runner.getDevice());
}
@Override
public void onClose(String key, PcapLiveRunner runner) {
runner.unsetUdpProcessor(Protocol.NETBIOS, netbios);
runner.unsetUdpProcessor(Protocol.DHCP, dhcp);
runner.getUdpDecoder().unregisterUdpProcessor(this);
logger.info("kraken ipmanager: disconnected pcap stream [{}], device [{}]", key, runner.getDevice());
}
@Override
public void process(UdpPacket p) {
if (p.getSource().getAddress() instanceof Inet6Address) {
logger.debug("kraken ipmanager: ipv6 address [{}] detected", p.getSource().getAddress());
return;
}
if (p.getSource().getAddress().getHostAddress().equals("0.0.0.0"))
return;
// assume ipv4 broadcast packet
IpPacket ip = p.getIpPacket();
EthernetFrame eth = (EthernetFrame) (ip.getL2Frame());
MacAddress sourceMac = eth.getSource();
InetAddress sourceIp = ip.getSourceAddress();
IpDetection d = new IpDetection("local", new Date(), sourceMac, sourceIp);
ipManager.updateIpEntry(d);
}
@Override
public void process(NetBiosNamePacket p) {
if (p.getUdpPacket().getSource().getAddress().getHostAddress().equals("0.0.0.0"))
return;
if (p.getHeader().getOpcode() == Opcode.Registration) {
NbResourceRecord rr = (NbResourceRecord) p.getData().getAdditionals().get(0);
String hostName = null;
String workGroup = null;
if (rr.getAddressList().get(0).getFlag() == 0)
hostName = rr.getName();
else
workGroup = rr.getName();
byte domainType = p.getData().getDomainType();
if (domainType != 0x20 && domainType == 0x00)
return;
IpPacket ipPacket = p.getUdpPacket().getIpPacket();
EthernetFrame frame = (EthernetFrame) ipPacket.getL2Frame();
ipManager.updateIpEntry(new IpDetection("local", new Date(), frame.getSource(),
ipPacket.getSourceAddress(), hostName, workGroup, null, null));
}
logger.trace("kraken ipmanager: netbios name packet [{}]", p);
}
@Override
public void process(DhcpMessage msg) {
Type type = DhcpOptions.getDhcpMessageType(msg);
MacAddress mac = msg.getClientMac();
InetAddress ip = msg.getClientAddress();
String workgroup = DhcpOptions.getDomainName(msg);
String category = null;
String vendor = null;
String hostName = DhcpOptions.getHostName(msg);
String fingerprint = DhcpOptions.getFingerprint(msg);
FingerprintMetadata metadata = FingerprintDetector.matches(fingerprint);
if (metadata != null) {
category = metadata.getCategory();
vendor = metadata.getVendor();
logger.trace("kraken ipmanager: dhcp [{}] metadata [{}]", hostName, metadata);
}
if (type == Type.Request) {
TemporaryIpDetection tid = new TemporaryIpDetection();
tid.mac = mac;
tid.hostName = hostName;
tid.workgroup = workgroup;
tid.category = category;
tid.vendor = vendor;
dhcpRequests.put(msg.getTransactionId(), tid);
reqTimestamps.add(new RequestTimestamp(msg.getTransactionId()));
} else if (type == Type.Inform) {
IpDetection d = new IpDetection("local", new Date(), mac, ip, hostName, workgroup, category, vendor);
ipManager.updateIpEntry(d);
} else if (type == Type.Ack) {
TemporaryIpDetection tid = dhcpRequests.get(msg.getTransactionId());
if (tid == null)
return;
mac = tid.mac;
ip = msg.getYourAddress();
hostName = tid.hostName;
workgroup = tid.workgroup;
category = tid.category;
vendor = tid.vendor;
dhcpRequests.remove(msg.getTransactionId());
IpDetection d = new IpDetection("local", new Date(), mac, ip, hostName, workgroup, category, vendor);
ipManager.updateIpEntry(d);
}
long now = System.currentTimeMillis();
while (!reqTimestamps.isEmpty() && now - reqTimestamps.peek().date.getTime() > 30000) {
RequestTimestamp rt = reqTimestamps.poll();
dhcpRequests.remove(rt.transactionId);
}
}
private class TemporaryIpDetection {
private MacAddress mac;
private String hostName;
private String workgroup;
private String category;
private String vendor;
}
private class RequestTimestamp {
private Integer transactionId;
private Date date = new Date();
private RequestTimestamp(Integer transactionId) {
this.transactionId = transactionId;
}
}
private class RequestTimestampComparator implements Comparator<RequestTimestamp> {
@Override
public int compare(RequestTimestamp o1, RequestTimestamp o2) {
return (int) (o1.date.getTime() - o2.date.getTime());
}
}
}