package org.batfish.grammar.iptables;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.batfish.datamodel.Ip;
import org.batfish.datamodel.IpProtocol;
import org.batfish.datamodel.Prefix;
import org.batfish.grammar.ControlPlaneExtractor;
import org.batfish.grammar.iptables.IptablesParser.Built_in_targetContext;
import org.batfish.grammar.iptables.IptablesParser.CommandContext;
import org.batfish.grammar.iptables.IptablesParser.Command_tailContext;
import org.batfish.grammar.iptables.IptablesParser.Declaration_chain_policyContext;
import org.batfish.grammar.iptables.IptablesParser.Declaration_tableContext;
import org.batfish.grammar.iptables.IptablesParser.EndpointContext;
import org.batfish.grammar.iptables.IptablesParser.Iptables_configurationContext;
import org.batfish.grammar.iptables.IptablesParser.MatchContext;
import org.batfish.grammar.iptables.IptablesParser.ProtocolContext;
import org.batfish.grammar.iptables.IptablesParser.Rule_specContext;
import org.batfish.common.Warnings;
import org.batfish.representation.iptables.IptablesChain.ChainPolicy;
import org.batfish.representation.iptables.IptablesConfiguration;
import org.batfish.representation.iptables.IptablesMatch.MatchType;
import org.batfish.representation.iptables.IptablesRule;
import org.batfish.representation.iptables.IptablesRule.IptablesActionType;
import org.batfish.vendor.VendorConfiguration;
import org.batfish.representation.iptables.IptablesVendorConfiguration;
public class IptablesControlPlaneExtractor extends IptablesParserBaseListener
implements ControlPlaneExtractor {
public static int toInteger(Token t) {
return Integer.parseInt(t.getText());
}
private IptablesConfiguration _configuration;
private String _fileName;
private boolean _formatIptablesSave = false;
private IptablesCombinedParser _parser;
private String _tableCurrent;
private String _text;
private final Set<String> _unimplementedFeatures;
private IptablesVendorConfiguration _vendorConfiguration;
private Warnings _w;
public IptablesControlPlaneExtractor(String fileText,
IptablesCombinedParser iptablesParser, Warnings warnings,
String fileName) {
_text = fileText;
_parser = iptablesParser;
_w = warnings;
_unimplementedFeatures = new TreeSet<>();
_fileName = fileName;
}
@Override
public void enterIptables_configuration(Iptables_configurationContext ctx) {
_vendorConfiguration = new IptablesVendorConfiguration();
_configuration = _vendorConfiguration;
_vendorConfiguration.setHostname(_fileName);
}
@Override
public void exitCommand(CommandContext ctx) {
// default table if not specified in the command
String table = (_formatIptablesSave) ? _tableCurrent : "filter";
if (ctx.table() != null) {
table = ctx.table().getText();
}
Command_tailContext tailCtx = ctx.command_tail();
if (tailCtx.command_append() != null) {
String chain = tailCtx.command_append().chain().getText();
IptablesRule rule = extractRule(tailCtx.command_append().rule_spec());
_configuration.addRule(table, chain, rule, -1);
}
else if (tailCtx.command_check() != null) {
todo(tailCtx.command_check(), "Command Check");
}
else if (tailCtx.command_delete() != null) {
todo(tailCtx.command_delete(), "Command Delete");
}
else if (tailCtx.command_delete_chain() != null) {
todo(tailCtx.command_delete_chain(), "Command Delete Chain");
}
else if (tailCtx.command_flush() != null) {
todo(tailCtx.command_flush(), "Command Flush");
}
else if (tailCtx.command_help() != null) {
todo(tailCtx.command_help(), "Command Help");
}
else if (tailCtx.command_insert() != null) {
String chain = tailCtx.command_append().chain().getText();
int ruleNum = 1;
if (tailCtx.command_insert().rulenum != null) {
ruleNum = toInteger(tailCtx.command_insert().rulenum);
}
IptablesRule rule = extractRule(tailCtx.command_insert().rule_spec());
_configuration.addRule(table, chain, rule, ruleNum);
}
else if (tailCtx.command_list() != null) {
todo(tailCtx.command_list(), "Command List");
}
else if (tailCtx.command_list_rules() != null) {
todo(tailCtx.command_list_rules(), "Command List Rules");
}
else if (tailCtx.command_new_chain() != null) {
String chain = tailCtx.command_new_chain().chain().getText();
_configuration.addChain(table, chain);
}
else if (tailCtx.command_policy() != null) {
String chain = tailCtx.command_policy().chain().getText();
ChainPolicy policy = getBuiltInTarget(
tailCtx.command_policy().built_in_target());
_configuration.setChainPolicy(table, chain, policy);
}
else if (tailCtx.command_rename_chain() != null) {
todo(tailCtx.command_rename_chain(), "Command Rename Chain");
}
else if (tailCtx.command_replace() != null) {
todo(tailCtx.command_replace(), "Command Replace");
}
else if (tailCtx.command_zero() != null) {
todo(tailCtx.command_zero(), "Command Zero");
}
else {
todo(tailCtx, "Unknown command");
}
}
@Override
public void exitDeclaration_chain_policy(
Declaration_chain_policyContext ctx) {
String chain = ctx.chain().getText();
ChainPolicy policy = getBuiltInTarget(ctx.built_in_target());
_configuration.setChainPolicy(_tableCurrent, chain, policy);
}
@Override
public void exitDeclaration_table(Declaration_tableContext ctx) {
_formatIptablesSave = true;
_tableCurrent = ctx.table().getText();
}
private IptablesRule extractRule(Rule_specContext ctx) {
IptablesRule rule = new IptablesRule();
List<MatchContext> matches = ctx.match_list;
for (MatchContext mCtx : matches) {
boolean inverted = (mCtx.NOT() != null);
if (mCtx.OPTION_IPV4() != null || mCtx.OPTION_IPV6() != null) {
todo(mCtx, "ipv4 (--4) and ipv6 (--6) options");
}
else if (mCtx.OPTION_DESTINATION() != null) {
rule.addMatch(inverted, MatchType.DESTINATION,
getEndpoint(mCtx.endpoint()));
}
else if (mCtx.OPTION_DESTINATION_PORT() != null) {
rule.addMatch(inverted, MatchType.DESTINATION_PORT,
toInteger(mCtx.port));
}
else if (mCtx.OPTION_IN_INTERFACE() != null) {
// rule.addMatch(inverted, MatchType.IN_INTERFACE,
// mCtx.interface_name.getText());
todo(mCtx, "matching in input interface");
}
else if (mCtx.OPTION_PROTOCOL() != null) {
rule.addMatch(inverted, MatchType.PROTOCOL,
toProtocol(mCtx.protocol()));
}
else if (mCtx.OPTION_OUT_INTERFACE() != null) {
// rule.addMatch(inverted, MatchType.OUT_INTERFACE,
// mCtx.interface_name.getText());
todo(mCtx, "matching on outgoing interface");
}
else if (mCtx.OPTION_SOURCE() != null) {
rule.addMatch(inverted, MatchType.SOURCE,
getEndpoint(mCtx.endpoint()));
}
else if (mCtx.OPTION_SOURCE_PORT() != null) {
rule.addMatch(inverted, MatchType.SOURCE_PORT,
toInteger(mCtx.port));
}
else {
todo(mCtx, "Unknown match option");
}
}
if (ctx.action().OPTION_JUMP() != null) {
if (ctx.action().built_in_target() != null) {
ChainPolicy policy = getBuiltInTarget(
ctx.action().built_in_target());
rule.setAction(policy);
}
else if (ctx.action().chain() != null) {
rule.setAction(IptablesActionType.CHAIN,
ctx.action().chain().getText());
}
}
else if (ctx.action().OPTION_GOTO() != null) {
rule.setAction(IptablesActionType.GOTO,
ctx.action().chain().getText());
}
else {
todo(ctx, "Unknown rule action");
}
return rule;
}
private ChainPolicy getBuiltInTarget(Built_in_targetContext ctx) {
if (ctx.ACCEPT() != null) {
return ChainPolicy.ACCEPT;
}
else if (ctx.DROP() != null) {
return ChainPolicy.DROP;
}
else if (ctx.RETURN() != null) {
return ChainPolicy.RETURN;
}
else {
todo(ctx, "Chain policy (built in target)");
}
return null;
}
private Object getEndpoint(EndpointContext endpoint) {
if (endpoint.IP_ADDRESS() != null) {
return new Ip(endpoint.IP_ADDRESS().getText());
}
else if (endpoint.IP_PREFIX() != null) {
return new Prefix(endpoint.IP_PREFIX().getText());
}
else if (endpoint.IPV6_ADDRESS() != null) {
// return new Ip6(endpoint.IPV6_ADDRESS().getText());
todo(endpoint, "IPV6 address as endpoint");
}
else if (endpoint.IPV6_PREFIX() != null) {
// return new Prefix6(endpoint.IPV6_PREFIX().getText());
todo(endpoint, "IPV6 prefix as endpoint");
}
else if (endpoint.name != null) {
// return endpoint.name.getText();
todo(endpoint, "hostname as endpoint");
}
else {
todo(endpoint, "Unknown endpoint");
}
return null;
}
@Override
public Set<String> getUnimplementedFeatures() {
return _unimplementedFeatures;
}
@Override
public VendorConfiguration getVendorConfiguration() {
return _vendorConfiguration;
}
@Override
public void processParseTree(ParserRuleContext tree) {
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(this, tree);
}
private void todo(ParserRuleContext ctx, String feature) {
_w.todo(ctx, feature, _parser, _text);
_w.unimplemented("Iptables : " + feature);
_unimplementedFeatures.add("Iptables: " + feature);
}
private IpProtocol toProtocol(ProtocolContext protocol) {
return IpProtocol.fromString(protocol.getText());
}
}