package org.batfish.representation.vyos;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Map.Entry;
import org.batfish.common.BatfishException;
import org.batfish.common.VendorConversionException;
import org.batfish.datamodel.Configuration;
import org.batfish.datamodel.ConfigurationFormat;
import org.batfish.datamodel.IkeGateway;
import org.batfish.datamodel.IkePolicy;
import org.batfish.datamodel.Ip;
import org.batfish.datamodel.IpsecPolicy;
import org.batfish.datamodel.IpsecProposal;
import org.batfish.datamodel.IpsecProtocol;
import org.batfish.datamodel.IpsecVpn;
import org.batfish.datamodel.LineAction;
import org.batfish.datamodel.Prefix;
import org.batfish.datamodel.RouteFilterLine;
import org.batfish.datamodel.RouteFilterList;
import org.batfish.datamodel.collections.RoleSet;
import org.batfish.datamodel.routing_policy.RoutingPolicy;
import org.batfish.datamodel.routing_policy.expr.Conjunction;
import org.batfish.datamodel.routing_policy.statement.If;
import org.batfish.datamodel.routing_policy.statement.Statement;
import org.batfish.datamodel.routing_policy.statement.Statements;
import org.batfish.vendor.VendorConfiguration;
public class VyosConfiguration extends VendorConfiguration {
/**
*
*/
private static final long serialVersionUID = 1L;
private BgpProcess _bgpProcess;
private Configuration _c;
private final Map<String, EspGroup> _espGroups;
private ConfigurationFormat _format;
protected String _hostname;
private final Map<String, IkeGroup> _ikeGroups;
private final Map<String, Interface> _interfaces;
private final Set<String> _ipsecInterfaces;
private final Map<Ip, IpsecPeer> _ipsecPeers;
private transient Map<Ip, org.batfish.datamodel.Interface> _ipToInterfaceMap;
private final Map<String, PrefixList> _prefixLists;
private final RoleSet _roles;
private final Map<String, RouteMap> _routeMaps;
private final Set<StaticNextHopRoute> _staticNextHopRoutes;
private transient Set<String> _unimplementedFeatures;
public VyosConfiguration() {
_roles = new RoleSet();
_espGroups = new TreeMap<>();
_ikeGroups = new TreeMap<>();
_interfaces = new TreeMap<>();
_ipsecInterfaces = new TreeSet<>();
_ipsecPeers = new TreeMap<>();
_prefixLists = new TreeMap<>();
_routeMaps = new TreeMap<>();
_staticNextHopRoutes = new HashSet<>();
}
private void convertInterfaces() {
for (Entry<String, Interface> e : _interfaces.entrySet()) {
String name = e.getKey();
Interface iface = e.getValue();
org.batfish.datamodel.Interface newIface = toInterface(iface);
_c.getDefaultVrf().getInterfaces().put(name, newIface);
}
}
private void convertPrefixLists() {
for (Entry<String, PrefixList> e : _prefixLists.entrySet()) {
String name = e.getKey();
PrefixList prefixList = e.getValue();
RouteFilterList routeFilterList = toRouteFilterList(prefixList);
_c.getRouteFilterLists().put(name, routeFilterList);
}
}
private void convertRouteMaps() {
for (Entry<String, RouteMap> e : _routeMaps.entrySet()) {
String name = e.getKey();
RouteMap routeMap = e.getValue();
RoutingPolicy rp = toRoutingPolicy(routeMap);
_c.getRoutingPolicies().put(name, rp);
}
}
private void convertVpns() {
for (Entry<Ip, IpsecPeer> ipsecPeerEntry : _ipsecPeers.entrySet()) {
// create ipsecvpn and ikegateway to correspond roughly to vyos ipsec
// site-to-site peer
Ip peerAddress = ipsecPeerEntry.getKey();
IpsecPeer ipsecPeer = ipsecPeerEntry.getValue();
String newIpsecVpnName = peerAddress.toString();
String newIkeGatewayName = newIpsecVpnName;
IpsecVpn newIpsecVpn = new IpsecVpn(newIpsecVpnName, _c);
_c.getIpsecVpns().put(newIpsecVpnName, newIpsecVpn);
IkeGateway newIkeGateway = new IkeGateway(newIkeGatewayName);
_c.getIkeGateways().put(newIkeGatewayName, newIkeGateway);
newIpsecVpn.setIkeGateway(newIkeGateway);
newIkeGateway.setLocalId(ipsecPeer.getAuthenticationId());
newIkeGateway.setRemoteId(ipsecPeer.getAuthenticationRemoteId());
newIkeGateway.setAddress(peerAddress);
Ip localAddress = ipsecPeer.getLocalAddress();
org.batfish.datamodel.Interface externalInterface = _ipToInterfaceMap
.get(localAddress);
if (externalInterface == null) {
_w.redFlag("Could not determine external interface for vpn \""
+ newIpsecVpnName + "\" from local-address: "
+ localAddress.toString());
}
else {
newIkeGateway.setExternalInterface(externalInterface);
}
// bind interface
String bindInterfaceName = ipsecPeer.getBindInterface();
org.batfish.datamodel.Interface newBindInterface = _c.getDefaultVrf()
.getInterfaces().get(bindInterfaceName);
if (newBindInterface != null) {
Interface bindInterface = _interfaces.get(bindInterfaceName);
bindInterface.getReferers().put(ipsecPeer,
"bind interface for site-to-site peer \"" + newIpsecVpnName
+ "\"");
newIpsecVpn.setBindInterface(newBindInterface);
}
else {
_w.redFlag("Reference to undefined bind-interface: \""
+ bindInterfaceName + "\"");
}
// convert the referenced ike group
String ikeGroupName = ipsecPeer.getIkeGroup();
IkeGroup ikeGroup = _ikeGroups.get(ikeGroupName);
if (ikeGroup == null) {
_w.redFlag(
"Reference to undefined ike-group: \"" + ikeGroupName + "\"");
}
else {
ikeGroup.getReferers().put(ipsecPeer,
"ike group for site-to-site peer: \"" + newIpsecVpnName
+ "\"");
IkePolicy newIkePolicy = new IkePolicy(ikeGroupName);
_c.getIkePolicies().put(ikeGroupName, newIkePolicy);
newIkeGateway.setIkePolicy(newIkePolicy);
newIkePolicy.setPreSharedKeyHash(
ipsecPeer.getAuthenticationPreSharedSecretHash());
// convert contained ike proposals
for (Entry<Integer, IkeProposal> ikeProposalEntry : ikeGroup
.getProposals().entrySet()) {
String newIkeProposalName = ikeGroupName + ":"
+ Integer.toString(ikeProposalEntry.getKey());
IkeProposal ikeProposal = ikeProposalEntry.getValue();
org.batfish.datamodel.IkeProposal newIkeProposal = new org.batfish.datamodel.IkeProposal(
newIkeProposalName, -1);
_c.getIkeProposals().put(newIkeProposalName, newIkeProposal);
newIkePolicy.getProposals().put(newIkeProposalName,
newIkeProposal);
newIkeProposal.setDiffieHellmanGroup(ikeProposal.getDhGroup());
newIkeProposal.setEncryptionAlgorithm(
ikeProposal.getEncryptionAlgorithm());
newIkeProposal.setLifetimeSeconds(ikeGroup.getLifetimeSeconds());
newIkeProposal.setAuthenticationAlgorithm(ikeProposal
.getHashAlgorithm().toIkeAuthenticationAlgorithm());
newIkeProposal.setAuthenticationMethod(
ipsecPeer.getAuthenticationMode());
}
}
// convert the referenced esp group
String espGroupName = ipsecPeer.getEspGroup();
EspGroup espGroup = _espGroups.get(espGroupName);
if (espGroup == null) {
_w.redFlag(
"Reference to undefined esp-group: \"" + espGroupName + "\"");
}
else {
espGroup.getReferers().put(ipsecPeer,
"esp-group for ipsec site-to-site peer: \"" + newIpsecVpnName
+ "\"");
IpsecPolicy newIpsecPolicy = new IpsecPolicy(espGroupName);
_c.getIpsecPolicies().put(espGroupName, newIpsecPolicy);
newIpsecVpn.setIpsecPolicy(newIpsecPolicy);
if (espGroup.getPfsSource() == null) {
espGroup.setPfsSource(PfsSource.IKE_GROUP);
}
switch (espGroup.getPfsSource()) {
case DISABLED:
break;
case ESP_GROUP:
newIpsecPolicy.setPfsKeyGroup(espGroup.getPfsDhGroup());
break;
case IKE_GROUP:
newIpsecPolicy.setPfsKeyGroupDynamicIke(true);
break;
default:
throw new BatfishException("Invalid pfs source");
}
// convert contained esp proposals
for (Entry<Integer, EspProposal> espProposalEntry : espGroup
.getProposals().entrySet()) {
String newIpsecProposalName = espGroupName + ":"
+ Integer.toString(espProposalEntry.getKey());
EspProposal espProposal = espProposalEntry.getValue();
IpsecProposal newIpsecProposal = new IpsecProposal(
newIpsecProposalName, -1);
_c.getIpsecProposals().put(newIpsecProposalName,
newIpsecProposal);
newIpsecPolicy.getProposals().put(newIpsecProposalName,
newIpsecProposal);
newIpsecProposal.setAuthenticationAlgorithm(espProposal
.getHashAlgorithm().toIpsecAuthenticationAlgorithm());
newIpsecProposal.setEncryptionAlgorithm(
espProposal.getEncryptionAlgorithm());
newIpsecProposal
.setLifetimeSeconds(espGroup.getLifetimeSeconds());
newIpsecProposal.setProtocol(IpsecProtocol.ESP);
}
}
}
}
public BgpProcess getBgpProcess() {
return _bgpProcess;
}
public Map<String, EspGroup> getEspGroups() {
return _espGroups;
}
@Override
public String getHostname() {
return _hostname;
}
public Map<String, IkeGroup> getIkeGroups() {
return _ikeGroups;
}
public Map<String, Interface> getInterfaces() {
return _interfaces;
}
public Set<String> getIpsecInterfaces() {
return _ipsecInterfaces;
}
public Map<Ip, IpsecPeer> getIpsecPeers() {
return _ipsecPeers;
}
public Map<String, PrefixList> getPrefixLists() {
return _prefixLists;
}
@Override
public RoleSet getRoles() {
return _roles;
}
public Map<String, RouteMap> getRouteMaps() {
return _routeMaps;
}
public Set<StaticNextHopRoute> getStaticNextHopRoutes() {
return _staticNextHopRoutes;
}
@Override
public Set<String> getUnimplementedFeatures() {
return _unimplementedFeatures;
}
public void setBgpProcess(BgpProcess bgpProcess) {
_bgpProcess = bgpProcess;
}
@Override
public void setHostname(String hostname) {
_hostname = hostname;
}
@Override
public void setRoles(RoleSet roles) {
_roles.addAll(roles);
}
@Override
public void setVendor(ConfigurationFormat format) {
_format = format;
}
private org.batfish.datamodel.Interface toInterface(Interface iface) {
String name = iface.getName();
org.batfish.datamodel.Interface newIface = new org.batfish.datamodel.Interface(
name, _c);
newIface.setActive(true); // TODO: may have to change
newIface.setBandwidth(iface.getBandwidth());
newIface.setDescription(iface.getDescription());
Prefix prefix = iface.getPrefix();
if (prefix != null) {
newIface.setPrefix(iface.getPrefix());
}
newIface.getAllPrefixes().addAll(iface.getAllPrefixes());
for (Prefix p : newIface.getAllPrefixes()) {
_ipToInterfaceMap.put(p.getAddress(), newIface);
}
return newIface;
}
private RouteFilterList toRouteFilterList(PrefixList prefixList) {
String name = prefixList.getName();
RouteFilterList newList = new RouteFilterList(name);
for (PrefixListRule rule : prefixList.getRules().values()) {
RouteFilterLine newLine = new RouteFilterLine(rule.getAction(),
rule.getPrefix(), rule.getLengthRange());
newList.getLines().add(newLine);
}
return newList;
}
private RoutingPolicy toRoutingPolicy(RouteMap routeMap) {
String name = routeMap.getName();
RoutingPolicy routingPolicy = new RoutingPolicy(name, _c);
List<Statement> statements = routingPolicy.getStatements();
for (Entry<Integer, RouteMapRule> e : routeMap.getRules().entrySet()) {
String ruleName = Integer.toString(e.getKey());
RouteMapRule rule = e.getValue();
If ifStatement = new If();
List<Statement> trueStatements = ifStatement.getTrueStatements();
ifStatement.setComment(ruleName);
Conjunction conj = new Conjunction();
for (RouteMapMatch match : rule.getMatches()) {
conj.getConjuncts().add(match.toBooleanExpr(this, _c, _w));
}
ifStatement.setGuard(conj.simplify());
switch (rule.getAction()) {
case ACCEPT:
trueStatements.add(Statements.ExitAccept.toStaticStatement());
break;
case REJECT:
trueStatements.add(Statements.ExitReject.toStaticStatement());
break;
default:
throw new BatfishException("Invalid action");
}
statements.add(ifStatement);
}
statements.add(Statements.ExitReject.toStaticStatement());
return routingPolicy;
}
@Override
public Configuration toVendorIndependentConfiguration()
throws VendorConversionException {
_ipToInterfaceMap = new HashMap<>();
_c = new Configuration(_hostname);
_c.setConfigurationFormat(_format);
_c.setDefaultCrossZoneAction(LineAction.ACCEPT);
_c.setDefaultInboundAction(LineAction.ACCEPT);
convertPrefixLists();
convertRouteMaps();
convertInterfaces();
convertVpns();
// TODO: convert routing processes
warnAndDisableUnreferencedVtiInterfaces();
return _c;
}
private void warnAndDisableUnreferencedVtiInterfaces() {
for (Entry<String, Interface> ifaceEntry : _interfaces.entrySet()) {
Interface iface = ifaceEntry.getValue();
if (iface.getType() == InterfaceType.VTI && iface.isUnused()) {
String name = ifaceEntry.getKey();
_c.getDefaultVrf().getInterfaces().remove(name);
_w.redFlag("Disabling unused VTI interface: \"" + name + "\"");
}
}
}
}