package org.batfish.representation.aws_vpcs;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.batfish.common.BatfishException;
import org.batfish.common.BatfishLogger;
import org.batfish.datamodel.BgpNeighbor;
import org.batfish.datamodel.BgpProcess;
import org.batfish.datamodel.Configuration;
import org.batfish.datamodel.DiffieHellmanGroup;
import org.batfish.datamodel.EncryptionAlgorithm;
import org.batfish.datamodel.IkeAuthenticationAlgorithm;
import org.batfish.datamodel.IkeAuthenticationMethod;
import org.batfish.datamodel.IkeGateway;
import org.batfish.datamodel.IkePolicy;
import org.batfish.datamodel.IkeProposal;
import org.batfish.datamodel.Interface;
import org.batfish.datamodel.IpsecAuthenticationAlgorithm;
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.RoutingProtocol;
import org.batfish.datamodel.StaticRoute;
import org.batfish.datamodel.SubRange;
import org.batfish.datamodel.routing_policy.RoutingPolicy;
import org.batfish.datamodel.routing_policy.expr.Conjunction;
import org.batfish.datamodel.routing_policy.expr.DestinationNetwork;
import org.batfish.datamodel.routing_policy.expr.MatchPrefixSet;
import org.batfish.datamodel.routing_policy.expr.MatchProtocol;
import org.batfish.datamodel.routing_policy.expr.NamedPrefixSet;
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.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class VpnConnection implements AwsVpcEntity, Serializable {
private static final int BGP_NEIGHBOR_DEFAULT_METRIC = 0;
private static final long serialVersionUID = 1L;
private static DiffieHellmanGroup toDiffieHellmanGroup(
String perfectForwardSecrecy) {
switch (perfectForwardSecrecy) {
case "group2":
return DiffieHellmanGroup.GROUP2;
default:
throw new BatfishException(
"No conversion to Diffie-Hellman group for string: \""
+ perfectForwardSecrecy + "\"");
}
}
private static EncryptionAlgorithm toEncryptionAlgorithm(
String encryptionProtocol) {
switch (encryptionProtocol) {
case "aes-128-cbc":
return EncryptionAlgorithm.AES_128_CBC;
default:
throw new BatfishException(
"No conversion to encryption algorithm for string: \""
+ encryptionProtocol + "\"");
}
}
private static IkeAuthenticationAlgorithm toIkeAuthenticationAlgorithm(
String ikeAuthProtocol) {
switch (ikeAuthProtocol) {
case "sha1":
return IkeAuthenticationAlgorithm.SHA1;
default:
throw new BatfishException(
"No conversion to ike authentication algorithm for string: \""
+ ikeAuthProtocol + "\"");
}
}
private static IpsecAuthenticationAlgorithm toIpsecAuthenticationAlgorithm(
String ipsecAuthProtocol) {
switch (ipsecAuthProtocol) {
case "hmac-sha1-96":
return IpsecAuthenticationAlgorithm.HMAC_SHA1_96;
default:
throw new BatfishException(
"No conversion to ipsec authentication algorithm for string: \""
+ ipsecAuthProtocol + "\"");
}
}
private static IpsecProtocol toIpsecProtocol(String ipsecProtocol) {
switch (ipsecProtocol) {
case "esp":
return IpsecProtocol.ESP;
default:
throw new BatfishException(
"No conversion to ipsec protocol for string: \"" + ipsecProtocol
+ "\"");
}
}
private final String _customerGatewayId;
private final List<IpsecTunnel> _ipsecTunnels;
private final List<Prefix> _routes;
private final boolean _staticRoutesOnly;
private final List<VgwTelemetry> _vgwTelemetrys;
private final String _vpnConnectionId;
private final String _vpnGatewayId;
public VpnConnection(JSONObject jObj, BatfishLogger logger)
throws JSONException {
_vgwTelemetrys = new LinkedList<>();
_routes = new LinkedList<>();
_ipsecTunnels = new LinkedList<>();
_vpnConnectionId = jObj.getString(JSON_KEY_VPN_CONNECTION_ID);
_customerGatewayId = jObj.getString(JSON_KEY_CUSTOMER_GATEWAY_ID);
_vpnGatewayId = jObj.getString(JSON_KEY_VPN_GATEWAY_ID);
logger.debugf("parsing vpnconnection id: %s\n", _vpnConnectionId);
String cgwConfiguration = jObj
.getString(JSON_KEY_CUSTOMER_GATEWAY_CONFIGURATION);
Document document;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(cgwConfiguration));
document = builder.parse(is);
}
catch (ParserConfigurationException | SAXException | IOException e) {
throw new BatfishException(
"Could not parse XML for CustomerGatewayConfiguration for vpn connection "
+ _vpnConnectionId + " " + e);
}
Element vpnConnection = (Element) document
.getElementsByTagName(XML_KEY_VPN_CONNECTION).item(0);
NodeList nodeList = document.getElementsByTagName(XML_KEY_IPSEC_TUNNEL);
for (int index = 0; index < nodeList.getLength(); index++) {
Element ipsecTunnel = (Element) nodeList.item(index);
_ipsecTunnels.add(new IpsecTunnel(ipsecTunnel, vpnConnection));
}
if (jObj.has(JSON_KEY_ROUTES)) {
JSONArray routes = jObj.getJSONArray(JSON_KEY_ROUTES);
for (int index = 0; index < routes.length(); index++) {
JSONObject childObject = routes.getJSONObject(index);
_routes.add(new Prefix(
childObject.getString(JSON_KEY_DESTINATION_CIDR_BLOCK)));
}
}
JSONArray vgwTelemetry = jObj.getJSONArray(JSON_KEY_VGW_TELEMETRY);
for (int index = 0; index < vgwTelemetry.length(); index++) {
JSONObject childObject = vgwTelemetry.getJSONObject(index);
_vgwTelemetrys.add(new VgwTelemetry(childObject, logger));
}
if (jObj.has(JSON_KEY_OPTIONS)) {
JSONObject options = jObj.getJSONObject(JSON_KEY_OPTIONS);
_staticRoutesOnly = Utils.tryGetBoolean(options,
JSON_KEY_STATIC_ROUTES_ONLY, false);
}
else {
_staticRoutesOnly = false;
}
}
public void applyToVpnGateway(AwsVpcConfiguration awsVpcConfiguration) {
Configuration vpnGatewayCfgNode = awsVpcConfiguration
.getConfigurationNodes().get(_vpnGatewayId);
for (int i = 0; i < _ipsecTunnels.size(); i++) {
int idNum = i + 1;
String vpnId = _vpnConnectionId + "-" + idNum;
IpsecTunnel ipsecTunnel = _ipsecTunnels.get(i);
if (ipsecTunnel.getCgwBgpAsn() != -1
&& (_staticRoutesOnly || _routes.size() != 0)) {
throw new BatfishException(
"Unexpected combination of BGP and static routes for VPN connection: \""
+ _vpnConnectionId + "\"");
}
// create representation structures and add to configuration node
IpsecVpn ipsecVpn = new IpsecVpn(vpnId, vpnGatewayCfgNode);
vpnGatewayCfgNode.getIpsecVpns().put(vpnId, ipsecVpn);
IpsecPolicy ipsecPolicy = new IpsecPolicy(vpnId);
vpnGatewayCfgNode.getIpsecPolicies().put(vpnId, ipsecPolicy);
ipsecVpn.setIpsecPolicy(ipsecPolicy);
IpsecProposal ipsecProposal = new IpsecProposal(vpnId, -1);
vpnGatewayCfgNode.getIpsecProposals().put(vpnId, ipsecProposal);
ipsecPolicy.getProposals().put(vpnId, ipsecProposal);
IkeGateway ikeGateway = new IkeGateway(vpnId);
vpnGatewayCfgNode.getIkeGateways().put(vpnId, ikeGateway);
ipsecVpn.setIkeGateway(ikeGateway);
IkePolicy ikePolicy = new IkePolicy(vpnId);
vpnGatewayCfgNode.getIkePolicies().put(vpnId, ikePolicy);
ikeGateway.setIkePolicy(ikePolicy);
IkeProposal ikeProposal = new IkeProposal(vpnId, -1);
vpnGatewayCfgNode.getIkeProposals().put(vpnId, ikeProposal);
ikePolicy.getProposals().put(vpnId, ikeProposal);
String externalInterfaceName = "external" + idNum;
Interface externalInterface = new Interface(externalInterfaceName,
vpnGatewayCfgNode);
vpnGatewayCfgNode.getDefaultVrf().getInterfaces()
.put(externalInterfaceName, externalInterface);
String vpnInterfaceName = "vpn" + idNum;
Interface vpnInterface = new Interface(vpnInterfaceName,
vpnGatewayCfgNode);
vpnGatewayCfgNode.getDefaultVrf().getInterfaces().put(vpnInterfaceName,
vpnInterface);
// Set fields within representation structures
// bind and vpn interfaces
Prefix externalInterfacePrefix = new Prefix(
ipsecTunnel.getVgwOutsideAddress(), 32);
externalInterface.setPrefix(externalInterfacePrefix);
externalInterface.getAllPrefixes().add(externalInterfacePrefix);
Prefix vpnInterfacePrefix = new Prefix(
ipsecTunnel.getVgwInsideAddress(),
ipsecTunnel.getVgwInsidePrefixLength());
vpnInterface.setPrefix(vpnInterfacePrefix);
vpnInterface.getAllPrefixes().add(vpnInterfacePrefix);
// ipsec
ipsecVpn.setBindInterface(vpnInterface);
ipsecPolicy.setPfsKeyGroup(toDiffieHellmanGroup(
ipsecTunnel.getIpsecPerfectForwardSecrecy()));
ipsecProposal
.setAuthenticationAlgorithm(toIpsecAuthenticationAlgorithm(
ipsecTunnel.getIpsecAuthProtocol()));
ipsecProposal.setEncryptionAlgorithm(
toEncryptionAlgorithm(ipsecTunnel.getIpsecEncryptionProtocol()));
ipsecProposal
.setProtocol(toIpsecProtocol(ipsecTunnel.getIpsecProtocol()));
ipsecProposal.setLifetimeSeconds(ipsecTunnel.getIpsecLifetime());
// ike
ikeGateway.setExternalInterface(externalInterface);
ikeGateway.setAddress(ipsecTunnel.getCgwOutsideAddress());
if (ipsecTunnel.getIkePreSharedKeyHash() != null) {
ikePolicy.setPreSharedKeyHash(ipsecTunnel.getIkePreSharedKeyHash());
ikeProposal.setAuthenticationMethod(
IkeAuthenticationMethod.PRE_SHARED_KEYS);
}
ikeProposal.setAuthenticationAlgorithm(
toIkeAuthenticationAlgorithm(ipsecTunnel.getIkeAuthProtocol()));
ikeProposal.setDiffieHellmanGroup(
toDiffieHellmanGroup(ipsecTunnel.getIkePerfectForwardSecrecy()));
ikeProposal.setEncryptionAlgorithm(
toEncryptionAlgorithm(ipsecTunnel.getIkeEncryptionProtocol()));
ikeProposal.setLifetimeSeconds(ipsecTunnel.getIkeLifetime());
// bgp (if configured)
if (ipsecTunnel.getVgwBgpAsn() != -1) {
BgpProcess proc = vpnGatewayCfgNode.getDefaultVrf().getBgpProcess();
if (proc == null) {
proc = new BgpProcess();
vpnGatewayCfgNode.getDefaultVrf().setBgpProcess(proc);
}
BgpNeighbor cgBgpNeighbor = new BgpNeighbor(
ipsecTunnel.getCgwInsideAddress(), vpnGatewayCfgNode);
cgBgpNeighbor.setVrf(Configuration.DEFAULT_VRF_NAME);
proc.getNeighbors().put(cgBgpNeighbor.getPrefix(), cgBgpNeighbor);
cgBgpNeighbor.setRemoteAs(ipsecTunnel.getCgwBgpAsn());
cgBgpNeighbor.setLocalAs(ipsecTunnel.getVgwBgpAsn());
cgBgpNeighbor.setLocalIp(ipsecTunnel.getVgwInsideAddress());
cgBgpNeighbor.setDefaultMetric(BGP_NEIGHBOR_DEFAULT_METRIC);
cgBgpNeighbor.setSendCommunity(false);
VpnGateway vpnGateway = awsVpcConfiguration.getVpnGateways()
.get(_vpnGatewayId);
List<String> attachmentVpcIds = vpnGateway.getAttachmentVpcIds();
if (attachmentVpcIds.size() != 1) {
throw new BatfishException(
"Not sure what routes to advertise since VPN Gateway: \""
+ _vpnGatewayId + "\" for VPN connection: \""
+ _vpnConnectionId
+ "\" is linked to multiple VPCs");
}
String vpcId = attachmentVpcIds.get(0);
Vpc vpc = awsVpcConfiguration.getVpcs().get(vpcId);
Prefix outgoingPrefix = vpc.getCidrBlock();
int outgoingPrefixLength = outgoingPrefix.getPrefixLength();
String originationPolicyName = vpnId + "_origination";
RoutingPolicy originationRoutingPolicy = new RoutingPolicy(
originationPolicyName, vpnGatewayCfgNode);
vpnGatewayCfgNode.getRoutingPolicies().put(originationPolicyName,
originationRoutingPolicy);
cgBgpNeighbor.setExportPolicy(originationPolicyName);
If originationIf = new If();
List<Statement> statements = originationRoutingPolicy
.getStatements();
statements.add(originationIf);
statements.add(Statements.ExitReject.toStaticStatement());
originationIf.getTrueStatements()
.add(Statements.ExitAccept.toStaticStatement());
RouteFilterList originationRouteFilter = new RouteFilterList(
originationPolicyName);
vpnGatewayCfgNode.getRouteFilterLists().put(originationPolicyName,
originationRouteFilter);
RouteFilterLine matchOutgoingPrefix = new RouteFilterLine(
LineAction.ACCEPT, outgoingPrefix,
new SubRange(outgoingPrefixLength, outgoingPrefixLength));
originationRouteFilter.addLine(matchOutgoingPrefix);
Conjunction conj = new Conjunction();
originationIf.setGuard(conj);
conj.getConjuncts().add(new MatchProtocol(RoutingProtocol.STATIC));
conj.getConjuncts().add(new MatchPrefixSet(new DestinationNetwork(),
new NamedPrefixSet(originationPolicyName)));
}
// static routes (if configured)
for (Prefix staticRoutePrefix : _routes) {
StaticRoute staticRoute = new StaticRoute(staticRoutePrefix,
ipsecTunnel.getCgwInsideAddress(), null,
Route.DEFAULT_STATIC_ROUTE_ADMIN,
Route.DEFAULT_STATIC_ROUTE_COST);
vpnGatewayCfgNode.getDefaultVrf().getStaticRoutes()
.add(staticRoute);
}
}
}
public String getCustomerGatewayId() {
return _customerGatewayId;
}
@Override
public String getId() {
return _vpnConnectionId;
}
public List<IpsecTunnel> getIpsecTunnels() {
return _ipsecTunnels;
}
public List<Prefix> getRoutes() {
return _routes;
}
public boolean getStaticRoutesOnly() {
return _staticRoutesOnly;
}
public List<VgwTelemetry> getVgwTelemetrys() {
return _vgwTelemetrys;
}
public String getVpnConnectionId() {
return _vpnConnectionId;
}
public String getVpnGatewayId() {
return _vpnGatewayId;
}
}