/*
* 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.captiveportal.impl;
import java.io.IOException;
import java.net.InetAddress;
import org.krakenapps.captiveportal.CaptivePortal;
import org.krakenapps.pcap.decoder.ethernet.EthernetDecoder;
import org.krakenapps.pcap.decoder.ethernet.EthernetFrame;
import org.krakenapps.pcap.decoder.ethernet.EthernetProcessor;
import org.krakenapps.pcap.decoder.ethernet.EthernetType;
import org.krakenapps.pcap.decoder.ethernet.MacAddress;
import org.krakenapps.pcap.live.PcapDevice;
import org.krakenapps.pcap.live.PcapDeviceManager;
import org.krakenapps.pcap.packet.PcapPacket;
import org.krakenapps.pcap.util.Buffer;
import org.krakenapps.pcap.util.IpConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FakeRouter implements Runnable, EthernetProcessor {
private final Logger logger = LoggerFactory.getLogger(FakeRouter.class.getName());
private Thread t;
private PcapDevice device;
private volatile boolean doStop;
private MacAddress localmac;
private CaptivePortal portal;
public FakeRouter(String deviceName, CaptivePortal portal) throws IOException {
this.device = PcapDeviceManager.open(deviceName, 1000000);
this.localmac = device.getMetadata().getMacAddress();
this.portal = portal;
}
public void start() {
t = new Thread(this, "Captive Portal Fake Router");
t.start();
}
public void stop() {
try {
doStop = true;
t.interrupt();
device.close();
} catch (IOException e) {
logger.error("kraken captive portal: cannot close device", e);
}
}
@Override
public void run() {
EthernetDecoder eth = new EthernetDecoder();
eth.register(EthernetType.IPV4, this);
doStop = false;
try {
while (!doStop) {
PcapPacket packet = device.getPacket();
eth.decode(packet);
}
} catch (IOException e) {
logger.error("kraken captive portal: routing io error", e);
} finally {
logger.info("kraken captive portal: fake router stopped");
}
}
@Override
public void process(EthernetFrame frame) {
Buffer buf = frame.getData();
buf.skip(9);
int protocol = buf.get();
buf.skip(2);
InetAddress src = IpConverter.toInetAddress(buf.getInt());
InetAddress dst = IpConverter.toInetAddress(buf.getInt());
int srcPort = buf.getUnsignedShort();
int dstPort = buf.getUnsignedShort();
if (dst.isMulticastAddress())
return;
buf.rewind();
byte[] b = new byte[buf.readableBytes() + 14];
buf.gets(b, 14, b.length - 14);
b[12] = 0x08;
b[13] = 0x00;
MacAddress srcmac = frame.getSource();
MacAddress dstmac = frame.getDestination();
MacAddress qsrcmac = portal.getQuarantinedMac(src);
MacAddress qdstmac = portal.getQuarantinedMac(dst);
MacAddress gwmac = portal.getGatewayMacAddress();
if (gwmac == null)
return;
if (srcmac.equals(localmac))
return;
// packet from target host
if (qsrcmac != null && !srcmac.equals(gwmac)) {
if (FakeDns.isDnsPacket(protocol, dstPort)) {
FakeDns.forgeResponse(portal.getRedirectAddress(), device, frame, src, srcPort, dst, dstPort, buf);
return;
}
// replace src mac -> local mac, dst mac -> real gateway mac, and
// forward it
byte[] d = gwmac.getBytes();
for (int i = 0; i < 6; i++)
b[i] = d[i];
byte[] s = localmac.getBytes();
for (int i = 0; i < 6; i++)
b[i + 6] = s[i];
sendPacket(b);
return;
}
// packet to target host
if (qdstmac != null && !dstmac.equals(gwmac)) {
if (FakeDns.isDnsPacket(protocol, dstPort)) {
FakeDns.forgeResponse(portal.getRedirectAddress(), device, frame, src, srcPort, dst, dstPort, buf);
return;
}
// replace src mac -> local mac, dst mac -> real host mac, and
// forward it
byte[] d = qdstmac.getBytes();
for (int i = 0; i < 6; i++)
b[i] = d[i];
byte[] s = localmac.getBytes();
for (int i = 0; i < 6; i++)
b[i + 6] = s[i];
sendPacket(b);
return;
}
}
private void sendPacket(byte[] b) {
try {
device.write(b);
} catch (IOException e) {
logger.error("kraken captive portal: cannot route packet", e);
}
}
}