IptablesRuleExecutor
package com.spawnlabs.server.dmfs.iptables;
import com.spawnlabs.server.dmfs.DmfsOperationException;
import org.apache.commons.lang.StringUtils;
import static com.spawnlabs.server.dmfs.DmfsNames.SPC;
import static com.spawnlabs.server.dmfs.DmfsNames.ls;
import static com.spawnlabs.server.dmfs.DmfsProcessException.*;
import static com.spawnlabs.server.dmfs.iptables.RuleExecutor.Command.iptables_restore_start;
import static com.spawnlabs.server.dmfs.iptables.RuleExecutor.Command.iptables_restore_stop;
import static com.spawnlabs.server.dmfs.iptables.RuleExecutor.Command.query_rule;
/**
*/
public class IptablesRuleExecutor
extends AbstractRuleExecutor {
// Exit code 2: Errors which appear to be caused by invalid or abused command line parameters
// Exit code 1: other errors
@Override
public DmfsExecuteResult applyStartRules(String rules) throws RuleExecutionException, IptablesStateViolationException {
try {
return runSynchronously(iptables_restore_start, rules, null, null, null);
} catch (DmfsExecuteException dee) {
if (dee.getExitValue() == 1) {
// Lemme guess: error message is "Process exited with an error: 1 (Exit value: 1)"
// And if you were starting a play session, it said: "line 2 failed". And that's all.
// Let's assume that, since we got this far, all arguments in this call were validated.
// And let's assume that, since we got an ExecuteException, our code didn't break.
// In that case, we have some kind of unspecified IptablesStateViolationException.
throw new IptablesStateViolationException(DmfsOperationException.IPTABLES_ADD_CONNECTION_ALREADY_EXISTS,
dee,
"Trying to add rules that already exist?",
rules);
}
throw new RuleExecutionException(IPTABLES_ERROR, dee, rules);
}
}
@Override
public DmfsExecuteResult applyStopRules(String rules) throws RuleExecutionException, IptablesStateViolationException {
try {
return runSynchronously(iptables_restore_stop, rules, null, null, null);
} catch (DmfsExecuteException dee) {
String errOut;
if (dee.getExitValue() == 2 && !StringUtils.isEmpty(errOut = dee.getErrorOut())) {
if (errOut.contains("Couldn't load target") && errOut.contains(":/lib64/iptables/libipt_")) {
// Trying to delete rules that no longer exist looks like this:
// -D PREROUTING -j XYZ
// After the -j is a "target". Each target corresonds to a kernel module that gets auto-loaded when you reference it.
// In this case, it's supposed to be a Chain which, if it existed, would be resolved as the target. If it doesn't exist
// iptables tries to load the module for target "XYZ". And, it can't.
// The error msg will look like:
// Couldn't load target `XYZ':/lib64/iptables/libipt_XYZ.so
// (Where 'XYZ' is the play session token.)
// This is a case of trying to delete a rule that doesn't exist
throw new IptablesStateViolationException(DmfsOperationException.IPTABLES_CONNECTION_DOESNT_EXIST,
dee,
"Trying to delete rules that no longer exist?",
rules);
}
}
throw new RuleExecutionException(IPTABLES_ERROR, dee, rules);
}
}
@Override
public DmfsExecuteResult queryPlaySession(String rules) throws RuleExecutionException, IptablesStateViolationException {
try {
return runSynchronously(query_rule, rules, null, null, null);
} catch (DmfsExecuteException dee) {
String errOut;
if (dee.getExitValue() == 1 && !StringUtils.isEmpty(errOut = dee.getErrorOut())) {
if (errOut.contains("iptables-restore: line 2 failed")) {
// In this case, we're listing rules based on token. The input's 1st two lines look like:
// *nat
// --list $token-- numeric-- verbose-- line - numbers
// If no chain by the name "$token" exists, that line fails.
// What this means for us is that they are querying a connection that doesn't exist.
// At this point in the stack, we don't know the token. Throw the exception up, and let the next level add that info.
throw new IptablesStateViolationException(DmfsOperationException.IPTABLES_CONNECTION_DOESNT_EXIST,
dee,
"Rule-listing failed.",
rules);
}
}
throw new RuleExecutionException(IPTABLES_ERROR, dee, rules);
}
}
@Override
public DmfsExecuteResult queryAllPlaySessions() throws RuleExecutionException {
DmfsExecuteResult result = super.queryAllPlaySessions();
String commandOutput;
if (!StringUtils.isBlank(commandOutput = result.getCommandOutput())) {
String[] lines = commandOutput.split(ls);
for (int i = 0, linesLength = lines.length; i < linesLength; i++) {
String line = lines[i];
if (!line.startsWith(CHAIN)) {
continue;
}
if (isABuiltInChain(line)) {
continue;
}
// If we get here, it's one of our connection chains
ConnectionInfo ci = new ConnectionInfo(getChainName(line), lines[i + 2]).invoke();
result.addConnectionInfo(ci);
}
}
return result;
}
protected static String getChainName(String line) {
//Chain ABC (1 references)
return line.split(SPC)[1];
}
protected static boolean isABuiltInChain(String line) {
return line.startsWith(CHAIN_S + CHAIN_FORWARD) || line.startsWith(CHAIN_S + CHAIN_OUTPUT) || line.startsWith(CHAIN_S + CHAIN_INPUT);
}
}