/*
* Copyright 2010 NCHOVY
*
* 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.iptables.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Provides;
import org.krakenapps.iptables.Chain;
import org.krakenapps.iptables.NetworkAddress;
import org.krakenapps.iptables.Rule;
import org.krakenapps.iptables.Iptables;
import org.krakenapps.iptables.match.IcmpMatchExtension;
import org.krakenapps.iptables.match.MatchExtension;
import org.krakenapps.iptables.match.MatchOption;
import org.krakenapps.iptables.match.MatchParseResult;
import org.krakenapps.iptables.match.StateMatchExtension;
import org.krakenapps.iptables.match.TcpMatchExtension;
import org.krakenapps.iptables.match.UdpMatchExtension;
import org.krakenapps.iptables.target.RejectTargetExtension;
import org.krakenapps.iptables.target.TargetExtension;
import org.krakenapps.iptables.target.TargetOption;
import org.krakenapps.iptables.target.TargetParseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(name = "iptables")
@Provides
public class IptablesService implements Iptables {
private final Logger logger = LoggerFactory.getLogger(IptablesService.class.getName());
private CommandRunner runner;
// option name and mapped extensions
private static final Map<String, MatchExtension> matchExtensions;
private static final Map<String, TargetExtension> targetExtensions;
static {
// match extensions
matchExtensions = new HashMap<String, MatchExtension>();
matchExtensions.put("tcp", new TcpMatchExtension());
matchExtensions.put("udp", new UdpMatchExtension());
matchExtensions.put("state", new StateMatchExtension());
matchExtensions.put("icmp", new IcmpMatchExtension());
// target extensions
targetExtensions = new HashMap<String, TargetExtension>();
targetExtensions.put("reject-with", new RejectTargetExtension());
}
public IptablesService() {
this.runner = new CommandRunnerImpl();
}
public void setCommandRunner(CommandRunner runner) {
this.runner = runner;
}
@Override
public List<String> getChainNames() throws IOException {
List<String> chains = new ArrayList<String>();
List<String> lines = runner.run("iptables -L -n -v");
for (String line : lines) {
if (line.startsWith("Chain ")) {
chains.add(line.split(" ")[1]);
}
}
return chains;
}
@Override
public List<Rule> getRules(String chainName) throws IOException {
List<Rule> rules = new ArrayList<Rule>();
List<String> lines = runner.run("iptables -L -n -v");
boolean found = false;
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
String[] tokens = line.split(" ");
if (tokens[0].equals("Chain")) {
found = tokens[1].equals(chainName);
i++;
continue;
}
if (!found)
continue;
if (line.length() == 0)
continue;
Scanner scanner = new Scanner(line);
// pkts and bytes
scanner.next();
scanner.next();
// target column
String target = scanner.next();
// prot column
String protocol = scanner.next();
// opt column
scanner.next();
// in and out
String in = scanner.next();
String out = scanner.next();
// source column
NetworkAddress source = new NetworkAddress(scanner.next());
// destination column
NetworkAddress destination = new NetworkAddress(scanner.next());
// matches
List<MatchOption> matchOptions = parseMatchExtensions(scanner, line);
// target extensions
List<TargetOption> targetOptions = parseTargetExtensions(scanner, line);
// create rule
Rule rule = new Rule();
if (!in.equals("*"))
rule.setIn(in);
if (!out.equals("*"))
rule.setOut(out);
if (!source.toString().equals("0.0.0.0/0"))
rule.setSource(source);
if (!destination.toString().equals("0.0.0.0/0"))
rule.setDestination(destination);
if (!protocol.equals("all"))
rule.setProtocol(protocol);
rule.setTarget(target);
rule.setMatchOptions(matchOptions);
rule.setTargetOptions(targetOptions);
rules.add(rule);
}
return rules;
}
private List<MatchOption> parseMatchExtensions(Scanner scanner, String line) {
List<MatchOption> matches = new ArrayList<MatchOption>();
int next = scanner.match().end() + 1;
int end = line.length();
while (next < end) {
if (line.charAt(next) != ' ')
break;
next++;
}
while (next < end) {
int begin = next;
int space = line.indexOf(' ', begin);
String type = line.substring(begin, space);
begin = space + 1;
MatchExtension ext = matchExtensions.get(type);
if (ext == null)
break;
MatchParseResult result = ext.parse(line, begin, end);
if (result == null)
break;
next = result.getNext();
matches.add(result.getOption());
}
return matches;
}
private List<TargetOption> parseTargetExtensions(Scanner scanner, String line) {
List<TargetOption> options = new ArrayList<TargetOption>();
int next = scanner.match().end() + 1;
int end = line.length();
while (next < end) {
if (line.charAt(next) != ' ')
break;
next++;
}
while (next < end) {
int begin = next;
int space = line.indexOf(' ', begin);
String type = line.substring(begin, space);
begin = space + 1;
TargetExtension ext = targetExtensions.get(type);
if (ext == null)
break;
TargetParseResult result = ext.parse(line, begin, end);
if (result == null)
break;
next = result.getNext();
options.add(result.getOption());
}
return options;
}
@Override
public void addRule(String chainName, int index, Rule rule) {
String cmd = String.format("iptables -I %s %d %s", chainName, index, rule);
logger.trace("kraken iptables: add rule [{}]", cmd);
try {
runner.run(cmd);
} catch (IOException e) {
logger.error("kraken iptables: failed to add rule.", e);
}
}
@Override
public void addRule(String chainName, Rule rule) {
String cmd = String.format("iptables -A %s %s", chainName, rule);
logger.trace("kraken iptables: add rule [{}]", cmd);
try {
runner.run(cmd);
} catch (IOException e) {
logger.error("kraken iptables: failed to add rule.", e);
}
}
@Override
public void removeRule(String chainName, int index) throws IOException {
String cmd = String.format("iptables -D %s %d", chainName, index);
runner.run(cmd);
}
@Override
public void addRule(Chain chain, int index, Rule rule) {
addRule(chain.name(), index, rule);
}
@Override
public void addRule(Chain chain, Rule rule) {
addRule(chain.name(), rule);
}
@Override
public List<Rule> getRules(Chain chain) throws IOException {
return getRules(chain.name());
}
@Override
public void removeRule(Chain chain, int index) throws IOException {
removeRule(chain.name(), index);
}
private static class CommandRunnerImpl implements CommandRunner {
@Override
public List<String> run(String cmdline) throws IOException {
List<String> lines = new ArrayList<String>();
Process child = Runtime.getRuntime().exec(cmdline);
try {
child.waitFor();
} catch (InterruptedException e) {
}
InputStream is = null;
if (child.exitValue() == 0)
is = child.getInputStream();
else
is = child.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while (true) {
String line = br.readLine();
if (line == null)
break;
lines.add(line);
}
if (child.exitValue() != 0)
throw new IllegalStateException(lines.get(0));
return lines;
}
}
}