You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2017/11/14 01:35:25 UTC

[trafficserver] branch master updated: YTSATS-1222: SNI based Configuration for traffic server

This is an automated email from the ASF dual-hosted git repository.

amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 22e6bf6  YTSATS-1222: SNI based Configuration for traffic server
22e6bf6 is described below

commit 22e6bf6f0e0db3bf57af6d9e33a4e210eb818640
Author: Persia Aziz <pe...@yahoo-inc.com>
AuthorDate: Tue May 2 17:05:23 2017 -0500

    YTSATS-1222: SNI based Configuration for traffic server
---
 Makefile.am                                        |   2 +-
 cmd/traffic_layout/traffic_layout.cc               |   1 +
 cmd/traffic_manager/AddConfigFilesHere.cc          |   1 +
 cmd/traffic_manager/traffic_manager.cc             |   2 +
 iocore/net/LuaSNIConfig.cc                         | 127 ++++++++++++
 iocore/net/LuaSNIConfig.h                          | 114 +++++++++++
 iocore/net/Makefile.am                             |  13 ++
 iocore/net/P_SNIActionPerformer.h                  | 100 +++++++++
 iocore/net/P_SSLConfig.h                           |   7 +-
 iocore/net/P_SSLNetVConnection.h                   |   8 +
 iocore/net/P_SSLSNI.h                              |  86 ++++++++
 iocore/net/P_SSLUtils.h                            |  63 ++++++
 iocore/net/SNIActionPerformer.cc                   |  53 +++++
 iocore/net/SSLCertLookup.cc                        |  19 --
 iocore/net/SSLConfig.cc                            |  14 +-
 iocore/net/SSLNetProcessor.cc                      |   4 +-
 iocore/net/SSLNetVConnection.cc                    |  91 ++++++---
 iocore/net/SSLSNIConfig.cc                         | 224 +++++++++++++++++++++
 iocore/net/SSLUtils.cc                             |  39 +++-
 lib/ts/Vec.h                                       |   5 +-
 lib/tsconfig/Makefile.am                           |   4 +-
 lib/tsconfig/TsConfigLua.cc                        |  53 +++++
 lib/tsconfig/TsConfigLua.h                         | 184 +++++++++++++++++
 mgmt/RecordsConfig.cc                              |   4 +
 mgmt/utils/MgmtUtils.h                             |   2 +
 proxy/Main.cc                                      |   4 +-
 proxy/Makefile.am                                  |   1 +
 proxy/config/Makefile.am                           |   1 +
 proxy/config/ssl_server_name.config.default        |  36 ++++
 proxy/http/HttpSM.cc                               |  70 ++++++-
 proxy/http/HttpTransact.cc                         |  44 ----
 proxy/http/remap/RemapConfig.cc                    |   2 +-
 proxy/http/remap/RemapProcessor.cc                 |   1 -
 .../gold_tests/autest-site/min_cfg/ssl_SNI.config  |   0
 .../gold_tests/autest-site/trafficserver.test.ext  |   4 +
 35 files changed, 1273 insertions(+), 110 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index b3f6b69..323f427 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,7 +29,7 @@ export CCACHE_BASEDIR
 # proxy/api/ts has to be built first, since so much of libraries and "core
 # depends on the generates ts/ts.h include file.
 
-SUBDIRS = proxy/api/ts iocore lib proxy/hdrs proxy/shared mgmt proxy cmd plugins tools example rc
+SUBDIRS = proxy/api/ts lib iocore proxy/hdrs proxy/shared mgmt proxy cmd plugins tools example rc
 if BUILD_DOCS
 SUBDIRS += doc
 endif
diff --git a/cmd/traffic_layout/traffic_layout.cc b/cmd/traffic_layout/traffic_layout.cc
index 34cd685..c0d2ef2 100644
--- a/cmd/traffic_layout/traffic_layout.cc
+++ b/cmd/traffic_layout/traffic_layout.cc
@@ -172,6 +172,7 @@ produce_layout(bool json)
   print_var("remap.config", RecConfigReadConfigPath("proxy.config.url_remap.filename"), json);
   print_var("plugin.config", RecConfigReadConfigPath(nullptr, "plugin.config"), json);
   print_var("ssl_multicert.config", RecConfigReadConfigPath("proxy.config.ssl.server.multicert.filename"), json);
+  print_var("ssl_server_name.config", RecConfigReadConfigPath("proxy.config.ssl.servername.filename"), json);
   print_var("storage.config", RecConfigReadConfigPath("proxy.config.cache.storage_filename"), json);
   print_var("hosting.config", RecConfigReadConfigPath("proxy.config.cache.hosting_filename"), json);
   print_var("volume.config", RecConfigReadConfigPath("proxy.config.cache.volume_filename"), json);
diff --git a/cmd/traffic_manager/AddConfigFilesHere.cc b/cmd/traffic_manager/AddConfigFilesHere.cc
index f620204..62caa38 100644
--- a/cmd/traffic_manager/AddConfigFilesHere.cc
+++ b/cmd/traffic_manager/AddConfigFilesHere.cc
@@ -77,5 +77,6 @@ initializeRegistry()
   configFiles->addFile("splitdns.config", false);
   configFiles->addFile("ssl_multicert.config", false);
   configFiles->addFile("metrics.config", false);
+  configFiles->addFile(SSL_SERVER_NAME_CONFIG, false);
   configFiles->registerCallback(testcall);
 }
diff --git a/cmd/traffic_manager/traffic_manager.cc b/cmd/traffic_manager/traffic_manager.cc
index 2d286ba..941c51b 100644
--- a/cmd/traffic_manager/traffic_manager.cc
+++ b/cmd/traffic_manager/traffic_manager.cc
@@ -962,6 +962,8 @@ fileUpdated(char *fname, bool incVersion)
     lmgmt->signalFileChange("proxy.config.http.congestion_control.filename");
   } else if (strcmp(fname, "proxy.config.ssl.server.ticket_key.filename") == 0) {
     lmgmt->signalFileChange("proxy.config.ssl.server.ticket_key.filename");
+  } else if (strcmp(fname, SSL_SERVER_NAME_CONFIG) == 0) {
+    lmgmt->signalFileChange("proxy.config.ssl.servername.filename");
   } else {
     mgmt_log("[fileUpdated] Unknown config file updated '%s'\n", fname);
   }
diff --git a/iocore/net/LuaSNIConfig.cc b/iocore/net/LuaSNIConfig.cc
new file mode 100644
index 0000000..13d5e37
--- /dev/null
+++ b/iocore/net/LuaSNIConfig.cc
@@ -0,0 +1,127 @@
+/** @file
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ */
+
+#include "LuaSNIConfig.h"
+#include <cstring>
+#include "ts/Diags.h"
+#include "P_SNIActionPerformer.h"
+#include "tsconfig/Errata.h"
+#include "tsconfig/TsConfigLua.h"
+
+TsConfigDescriptor LuaSNIConfig::desc = {TsConfigDescriptor::Type::ARRAY, "Array", "Item vector", "Vector"};
+TsConfigArrayDescriptor LuaSNIConfig::DESCRIPTOR(LuaSNIConfig::desc);
+TsConfigDescriptor LuaSNIConfig::Item::FQDN_DESCRIPTOR = {TsConfigDescriptor::Type::STRING, "String", TS_fqdn,
+                                                          "Fully Qualified Domain Name"};
+TsConfigDescriptor LuaSNIConfig::Item::DISABLE_h2_DESCRIPTOR = {TsConfigDescriptor::Type::BOOL, "Boolean", TS_disable_H2,
+                                                                "Disable H2"};
+TsConfigEnumDescriptor LuaSNIConfig::Item::LEVEL_DESCRIPTOR = {TsConfigDescriptor::Type::ENUM,
+                                                               "enum",
+                                                               "Level",
+                                                               "Level for client verification",
+                                                               {{"NONE", 0}, {"MODERATE", 1}, {"STRICT", 2}}};
+TsConfigDescriptor LuaSNIConfig::Item::TUNNEL_DEST_DESCRIPTOR = {TsConfigDescriptor::Type::STRING, "String", TS_tunnel_route,
+                                                                 "tunnel route destination"};
+TsConfigDescriptor LuaSNIConfig::Item::CLIENT_CERT_DESCRIPTOR = {TsConfigDescriptor::Type::STRING, "String", TS_client_cert,
+                                                                 "Client certificate to present to the next hop server"};
+TsConfigDescriptor LuaSNIConfig::Item::VERIFY_NEXT_SERVER_DESCRIPTOR = {TsConfigDescriptor::Type::INT, "Int",
+                                                                        TS_verify_origin_server, "Next hop verification level"};
+
+ts::Errata
+LuaSNIConfig::loader(lua_State *L)
+{
+  ts::Errata zret;
+  //  char buff[256];
+  //  int error;
+
+  lua_getfield(L, LUA_GLOBALSINDEX, "server_config");
+  int l_type = lua_type(L, -1);
+
+  switch (l_type) {
+  case LUA_TTABLE: // this has to be a multidimensional table
+    lua_pushnil(L);
+    while (lua_next(L, -2) != 0) {
+      l_type = lua_type(L, -1);
+      if (l_type == LUA_TTABLE) { // the item should be table
+        // new Item
+        LuaSNIConfig::Item item;
+        item.loader(L);
+        items.push_back(item);
+      } else {
+        zret.push(ts::Errata::Message(0, 0, "Invalid Entry at SNI config"));
+      }
+      lua_pop(L, 1);
+    }
+    break;
+  case LUA_TSTRING:
+    Debug("ssl", "string value %s", lua_tostring(L, -1));
+    break;
+  default:
+    zret.push(ts::Errata::Message(0, 0, "Invalid Lua SNI Config"));
+    Debug("ssl", "Please check your SNI config");
+    break;
+  }
+
+  return zret;
+}
+
+ts::Errata
+LuaSNIConfig::Item::loader(lua_State *L)
+{
+  ts::Errata zret;
+  //-1 will contain the subarray now (since it is a value in the main table))
+  lua_pushnil(L);
+  while (lua_next(L, -2)) {
+    if (lua_type(L, -2) != LUA_TSTRING) {
+      Debug("ssl", "string keys expected for entries in %s", SSL_SERVER_NAME_CONFIG);
+    }
+    const char *name = lua_tostring(L, -2);
+    if (!strncmp(name, TS_fqdn, strlen(TS_fqdn))) {
+      FQDN_CONFIG.loader(L);
+    } else if (!strncmp(name, TS_disable_H2, strlen(TS_disable_H2))) {
+      DISABLEH2_CONFIG.loader(L);
+    } else if (!strncmp(name, TS_verify_client, strlen(TS_verify_client))) {
+      VERIFYCLIENT_CONFIG.loader(L);
+    } else if (!strncmp(name, TS_verify_origin_server, strlen(TS_verify_origin_server))) {
+      VERIFY_NEXT_SERVER_CONFIG.loader(L);
+    } else if (!strncmp(name, TS_client_cert, strlen(TS_client_cert))) {
+      CLIENT_CERT_CONFIG.loader(L);
+    } else if (!strncmp(name, TS_tunnel_route, strlen(TS_tunnel_route))) {
+      TUNNEL_DEST_CONFIG.loader(L);
+    } else {
+      zret.push(ts::Errata::Message(0, 0, "Invalid Entry at SNI config"));
+    }
+    lua_pop(L, 1);
+  }
+  return zret;
+}
+
+ts::Errata
+LuaSNIConfig::registerEnum(lua_State *L)
+{
+  ts::Errata zret;
+  lua_newtable(L);
+  lua_setglobal(L, "LevelTable");
+  int i = start;
+  LUA_ENUM(L, "NONE", i++);
+  LUA_ENUM(L, "MODERATE", i++);
+  LUA_ENUM(L, "STRICT", i++);
+  return zret;
+}
diff --git a/iocore/net/LuaSNIConfig.h b/iocore/net/LuaSNIConfig.h
new file mode 100644
index 0000000..06330e5
--- /dev/null
+++ b/iocore/net/LuaSNIConfig.h
@@ -0,0 +1,114 @@
+/** @file
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+*/
+/*
+ * File:   LuaSNIConfig.h
+ * Author: persia
+ *
+ * Created on September 21, 2017, 4:25 PM
+ */
+
+#ifndef LUASNICONFIG_H
+#define LUASNICONFIG_H
+
+#include "tsconfig/TsConfigLua.h"
+
+#include <vector>
+
+using ts::Errata;
+#define LUA_ENUM(L, name, val)                  \
+  lua_pushlstring(L, #name, sizeof(#name) - 1); \
+  lua_pushnumber(L, val);                       \
+  lua_settable(L, -3);
+
+constexpr char TS_fqdn[]                 = "fqdn";
+constexpr char TS_disable_H2[]           = "disable_h2";
+constexpr char TS_verify_client[]        = "verify_client";
+constexpr char TS_tunnel_route[]         = "tunnel_route";
+constexpr char TS_verify_origin_server[] = "verify_origin_server";
+constexpr char TS_client_cert[]          = "client_cert";
+
+const int start = 0;
+struct LuaSNIConfig : public TsConfigBase {
+  using self = LuaSNIConfig;
+  enum class Action {
+    disable_h2 = start,
+    verify_client,
+    tunnel_route,         // blind tunnel action
+    verify_origin_server, // this applies to server side vc only
+    client_cert
+
+  };
+  enum class Level { NONE = 0, MODERATE, STRICT };
+  static TsConfigDescriptor desc;
+  static TsConfigArrayDescriptor DESCRIPTOR;
+
+  LuaSNIConfig() : TsConfigBase(this->DESCRIPTOR) { self::Item::Initialize(); }
+
+  struct Item : public TsConfigBase {
+    Item()
+      : TsConfigBase(DESCRIPTOR),
+        FQDN_CONFIG(FQDN_DESCRIPTOR, fqdn),
+        DISABLEH2_CONFIG(DISABLE_h2_DESCRIPTOR, disable_h2),
+        VERIFYCLIENT_CONFIG(LEVEL_DESCRIPTOR, (int &)verify_client_level),
+        TUNNEL_DEST_CONFIG(TUNNEL_DEST_DESCRIPTOR, tunnel_destination),
+        CLIENT_CERT_CONFIG(CLIENT_CERT_DESCRIPTOR, client_cert),
+        VERIFY_NEXT_SERVER_CONFIG(VERIFY_NEXT_SERVER_DESCRIPTOR, (int &)verify_origin_server)
+    {
+    }
+    ts::Errata loader(lua_State *s) override;
+    static void
+    Initialize()
+    {
+      //      ACTION_DESCRIPTOR.values = {
+      //        {TS_disable_H2, 0}, {TS_verify_client, 1}, {TS_tunnel_route, 2}, {TS_verify_origin_server, 3}, {TS_client_cert, 4}};
+      //
+      //      ACTION_DESCRIPTOR.keys = {
+      //        {0, TS_disable_H2}, {1, TS_verify_client}, {2, TS_tunnel_route}, {3, TS_verify_origin_server}, {4, TS_client_cert}};
+    }
+
+    std::string fqdn;
+    bool disable_h2             = false;
+    uint8_t verify_client_level = 0;
+    std::string tunnel_destination;
+    uint8_t verify_origin_server = 0;
+    std::string client_cert;
+
+    // These need to be initialized statically.
+    static TsConfigObjectDescriptor OBJ_DESCRIPTOR;
+    static TsConfigDescriptor FQDN_DESCRIPTOR;
+    TsConfigString FQDN_CONFIG;
+    static TsConfigDescriptor DISABLE_h2_DESCRIPTOR;
+    TsConfigBool DISABLEH2_CONFIG;
+    static TsConfigEnumDescriptor LEVEL_DESCRIPTOR;
+    TsConfigEnum<self::Level> VERIFYCLIENT_CONFIG;
+    static TsConfigDescriptor TUNNEL_DEST_DESCRIPTOR;
+    TsConfigString TUNNEL_DEST_CONFIG;
+    static TsConfigDescriptor CLIENT_CERT_DESCRIPTOR;
+    TsConfigString CLIENT_CERT_CONFIG;
+    static TsConfigDescriptor VERIFY_NEXT_SERVER_DESCRIPTOR;
+    TsConfigInt VERIFY_NEXT_SERVER_CONFIG;
+    ~Item() {}
+  };
+  std::vector<self::Item> items;
+  ts::Errata loader(lua_State *s) override;
+  ts::Errata registerEnum(lua_State *L);
+};
+#endif /* LUASNICONFIG_H */
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index 1f28b06..aaa7e94 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -50,6 +50,7 @@ test_certlookup_LDADD = \
 
 test_UDPNet_CPPFLAGS = \
   $(AM_CPPFLAGS) \
+  $(LUAJIT_CPPFLAGS) \
   $(iocore_include_dirs) \
   -I$(abs_top_srcdir)/proxy \
   -I$(abs_top_srcdir)/proxy/hdrs \
@@ -70,6 +71,8 @@ test_UDPNet_LDADD = \
   $(top_builddir)/lib/records/librecords_p.a \
   $(top_builddir)/lib/ts/libtsutil.la \
   $(top_builddir)/proxy/ParentSelectionStrategy.o \
+  $(top_builddir)/lib/tsconfig/libtsconfig.la \
+  $(top_builddir)/lib/luajit/src/libluajit.a \
   @LIBTCL@ @HWLOC_LIBS@ @OPENSSL_LIBS@
 
 test_UDPNet_SOURCES = \
@@ -89,8 +92,11 @@ libinknet_a_SOURCES = \
   Inline.cc \
   I_SessionAccept.h \
   SessionAccept.cc \
+  LuaSNIConfig.h \
+  LuaSNIConfig.cc \
   Net.cc \
   NetVConnection.cc \
+  P_SNIActionPerformer.h \
   P_CompletionUtil.h \
   P_Connection.h \
   P_InkBulkIO.h \
@@ -106,6 +112,7 @@ libinknet_a_SOURCES = \
   P_SSLNetVConnection.h \
   P_SSLNextProtocolAccept.h \
   P_SSLNextProtocolSet.h \
+  P_SSLSNI.h \
   P_SSLUtils.h \
   P_SSLClientUtils.h \
   P_OCSPStapling.h \
@@ -122,6 +129,7 @@ libinknet_a_SOURCES = \
   P_UnixPollDescriptor.h \
   P_UnixUDPConnection.h \
   Socks.cc \
+  SNIActionPerformer.cc \
   SSLCertLookup.cc \
   SSLSessionCache.cc \
   SSLConfig.cc \
@@ -131,6 +139,7 @@ libinknet_a_SOURCES = \
   SSLNetVConnection.cc \
   SSLNextProtocolAccept.cc \
   SSLNextProtocolSet.cc \
+  SSLSNIConfig.cc \
   SSLUtils.cc \
   SSLClientUtils.cc \
   OCSPStapling.cc \
@@ -157,6 +166,10 @@ if BUILD_TESTS
      P_NetVCTest.h
 endif
 
+libinknet_a_LIBADD = \
+   $(top_builddir)/lib/luajit/src/libluajit.a \
+   $(top_builddir)/lib/tsconfig/libtsconfig.la
+
 include $(top_srcdir)/build/tidy.mk
 
 tidy-local: $(DIST_SOURCES)
diff --git a/iocore/net/P_SNIActionPerformer.h b/iocore/net/P_SNIActionPerformer.h
new file mode 100644
index 0000000..803e09a
--- /dev/null
+++ b/iocore/net/P_SNIActionPerformer.h
@@ -0,0 +1,100 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ */
+
+/*************************** -*- Mod: C++ -*- ******************************
+  P_ActionProcessor.h
+   Created On      : 05/02/2017
+
+   Description:
+   SNI based Configuration in ATS
+ ****************************************************************************/
+#ifndef __P_ACTIONPROCESSOR_H__
+#define __P_ACTIONPROCESSOR_H__
+
+#include "I_EventSystem.h"
+#include "ts/Map.h"
+//#include"P_UnixNetProcessor.h"
+#include <vector>
+#include "P_SSLNextProtocolAccept.h"
+
+extern Map<int, SSLNextProtocolSet *> snpsMap;
+// enum of all the actions
+enum AllActions {
+  TS_DISABLE_H2 = 0,
+  TS_VERIFY_CLIENT, // this applies to server side vc only
+  TS_TUNNEL_ROUTE,  // blind tunnel action
+};
+
+/** action for setting next hop properties should be listed in the following enum*/
+enum PropertyActions { TS_VERIFY_SERVER = 200, TS_CLIENT_CERT };
+
+class ActionItem
+{
+public:
+  virtual void SNIAction(Continuation *cont) = 0;
+  virtual ~ActionItem(){};
+};
+
+class DisableH2 : public ActionItem
+{
+public:
+  DisableH2() {}
+  ~DisableH2() override {}
+
+  void
+  SNIAction(Continuation *cont) override
+  {
+    auto ssl_vc     = reinterpret_cast<SSLNetVConnection *>(cont);
+    auto accept_obj = ssl_vc->accept_object;
+    if (accept_obj && accept_obj->snpa && ssl_vc) {
+      auto nps = snpsMap.get(accept_obj->id);
+      ssl_vc->registerNextProtocolSet(reinterpret_cast<SSLNextProtocolSet *>(nps));
+    }
+  }
+};
+
+class VerifyClient : public ActionItem
+{
+  uint8_t mode;
+
+public:
+  VerifyClient(const char *param) : mode(atoi(param)) {}
+  VerifyClient(uint8_t param) : mode(param) {}
+  ~VerifyClient() override {}
+  void
+  SNIAction(Continuation *cont) override
+  {
+    auto ssl_vc = reinterpret_cast<SSLNetVConnection *>(cont);
+    Debug("ssl_sni", "action verify param %d", this->mode);
+    setClientCertLevel(ssl_vc->ssl, this->mode);
+  }
+};
+
+class SNIActionPerformer
+{
+public:
+  SNIActionPerformer() = default;
+  static void PerformAction(Continuation *cont, cchar *servername);
+};
+
+#endif
diff --git a/iocore/net/P_SSLConfig.h b/iocore/net/P_SSLConfig.h
index f15df36..c16af27 100644
--- a/iocore/net/P_SSLConfig.h
+++ b/iocore/net/P_SSLConfig.h
@@ -84,7 +84,7 @@ struct SSLConfigParams : public ConfigInfo {
   char *clientKeyPath;
   char *clientCACertFilename;
   char *clientCACertPath;
-  int clientVerify;
+  int8_t clientVerify;
   int client_verify_depth;
   long ssl_ctx_options;
   long ssl_client_ctx_protocols;
@@ -101,6 +101,7 @@ struct SSLConfigParams : public ConfigInfo {
   static size_t session_cache_number_buckets;
   static size_t session_cache_max_bucket_size;
   static bool session_cache_skip_on_lock_contention;
+  static bool sni_map_enable;
 
   // TS-3435 Wiretracing for SSL Connections
   static int ssl_wire_trace_enabled;
@@ -120,10 +121,10 @@ struct SSLConfigParams : public ConfigInfo {
   SSL_CTX *getCTX(cchar *client_cert) const;
   void deleteKey(cchar *key) const;
   void freeCTXmap() const;
-  void printCTXmap();
+  void printCTXmap() const;
   bool InsertCTX(cchar *client_cert, SSL_CTX *cctx) const;
   SSL_CTX *getClientSSL_CTX(void) const;
-  SSL_CTX *getNewCTX(char *client_cert) const;
+  SSL_CTX *getNewCTX(cchar *client_cert) const;
 
   void initialize();
   void cleanup();
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index f885184..805b231 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -167,6 +167,12 @@ public:
     return transparentPassThrough;
   }
 
+  bool
+  GetSNIMapping()
+  {
+    return SNIMapping;
+  }
+
   void
   setTransparentPassThrough(bool val)
   {
@@ -296,6 +302,7 @@ public:
   ink_hrtime sslHandshakeEndTime   = 0;
   ink_hrtime sslLastWriteTime      = 0;
   int64_t sslTotalBytesSent        = 0;
+  char *serverName                 = nullptr;
 
   /// Set by asynchronous hooks to request a specific operation.
   SslVConnOp hookOpRequested = SSL_HOOK_OP_DEFAULT;
@@ -335,6 +342,7 @@ private:
   Continuation *npnEndpoint        = nullptr;
   SessionAccept *sessionAcceptPtr  = nullptr;
   bool sslTrace                    = false;
+  bool SNIMapping                  = false;
 };
 
 typedef int (SSLNetVConnection::*SSLNetVConnHandler)(int, void *);
diff --git a/iocore/net/P_SSLSNI.h b/iocore/net/P_SSLSNI.h
new file mode 100644
index 0000000..cbc2825
--- /dev/null
+++ b/iocore/net/P_SSLSNI.h
@@ -0,0 +1,86 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ */
+
+/*************************** -*- Mod: C++ -*- ******************************
+  P_SSLSNI.h
+   Created On      : 05/02/2017
+
+   Description:
+   SNI based Configuration in ATS
+ ****************************************************************************/
+#ifndef __P_SSL_SNI_H__
+#define __P_SSL_SNI_H__
+
+#include "ProxyConfig.h"
+#include "ts/Map.h"
+#include "P_SNIActionPerformer.h"
+#include "ts/MatcherUtils.h"
+#include "openssl/ossl_typ.h"
+#include <vector>
+#include <unordered_map>
+#include <strings.h>
+#include "LuaSNIConfig.h"
+
+// Properties for the next hop server
+struct NextHopProperty {
+  const char *name   = nullptr; // name of the server
+  int8_t verifyLevel = 0;       // whether to verify the next hop
+  SSL_CTX *ctx       = nullptr; // ctx generated off the certificate to present to this server
+  NextHopProperty();
+};
+
+typedef std::vector<ActionItem *> actionVector;
+typedef HashMap<cchar *, StringHashFns, actionVector *> SNIMap;
+typedef HashMap<cchar *, StringHashFns, NextHopProperty *> NextHopPropertyTable;
+
+struct SNIConfigParams : public ConfigInfo {
+  char *sni_filename = nullptr;
+  SNIMap sni_action_map;
+  SNIMap wild_sni_action_map;
+  NextHopPropertyTable next_hop_table;
+  NextHopPropertyTable wild_next_hop_table;
+  LuaSNIConfig L_sni;
+  NextHopProperty *getPropertyConfig(cchar *servername) const;
+  SNIConfigParams();
+  virtual ~SNIConfigParams();
+  void cleanup();
+  int Initialize();
+  void loadSNIConfig();
+  actionVector *get(cchar *servername) const;
+  void printSNImap() const;
+};
+
+struct SNIConfig {
+  static void startup();
+  static void reconfigure();
+  static SNIConfigParams *acquire();
+  static void release(SNIConfigParams *params);
+  static void cloneProtoSet(); // clones the protoset of all the netaccept objects created and unregisters h2
+
+  typedef ConfigProcessor::scoped_config<SNIConfig, SNIConfigParams> scoped_config;
+
+private:
+  static int configid;
+};
+
+#endif
\ No newline at end of file
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index a3d530d..62c40ff 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -171,6 +171,8 @@ void SSLNetVCDetach(SSL *ssl);
 // Return the SSLNetVConnection (if any) attached to this SSL session.
 SSLNetVConnection *SSLNetVCAccess(const SSL *ssl);
 
+void setClientCertLevel(SSL *ssl, uint8_t certLevel);
+
 namespace ssl
 {
 namespace detail
@@ -215,6 +217,67 @@ namespace detail
 /* namespace ssl */ } /* namespace detail */
 }
 
+struct ats_wildcard_matcher {
+  ats_wildcard_matcher()
+  {
+    if (regex.compile("^\\*\\.[^\\*.]+") != 0) {
+      Fatal("failed to compile TLS wildcard matching regex");
+    }
+  }
+
+  ~ats_wildcard_matcher() {}
+  bool
+  match(const char *hostname) const
+  {
+    return regex.match(hostname) != -1;
+  }
+
+private:
+  DFA regex;
+};
+
+class TunnelHashMap
+{
+public:
+  struct HostStruct {
+    cchar *hostname;
+    int len;
+    int port;
+    HostStruct(cchar *name, int hostnamelen, int port_)
+    {
+      hostname = name;
+      len      = hostnamelen;
+      port     = port_;
+    }
+  };
+
+  typedef HashMap<cchar *, StringHashFns, const HostStruct *> Tunnel_hashMap;
+  Tunnel_hashMap TunnelhMap;
+
+  void
+  emplace(cchar *key, std::string &hostname)
+  {
+    ts::string_view addr, port;
+    if (ats_ip_parse(ts::string_view(hostname), &addr, &port) == 0) {
+      auto *hs = new HostStruct(ats_strdup(addr.data()), addr.length(), atoi(port.data()));
+      TunnelhMap.put(ats_strdup(key), hs);
+    }
+  }
+
+  void
+  emplace(cchar *key, cchar *name, int hostnamelen, int port_)
+  {
+    auto *hs = new HostStruct(ats_strdup(name), hostnamelen, port_);
+    TunnelhMap.put(ats_strdup(key), hs);
+  }
+
+  const HostStruct *
+  find(cchar *key)
+  {
+    return TunnelhMap.get(key);
+  }
+};
+
 typedef ats_scoped_resource<ssl::detail::SCOPED_X509_TRAITS> scoped_X509;
 typedef ats_scoped_resource<ssl::detail::SCOPED_BIO_TRAITS> scoped_BIO;
 
diff --git a/iocore/net/SNIActionPerformer.cc b/iocore/net/SNIActionPerformer.cc
new file mode 100644
index 0000000..b14e9c9
--- /dev/null
+++ b/iocore/net/SNIActionPerformer.cc
@@ -0,0 +1,53 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ */
+
+/*************************** -*- Mod: C++ -*- ******************************
+ SNIActionPerformer.cc
+   Created On      : 05/02/2017
+
+   Description:
+   SNI based Configuration in ATS
+ ****************************************************************************/
+#include "P_SNIActionPerformer.h"
+#include "ts/ink_memory.h"
+#include "P_SSLSNI.h"
+#include "P_Net.h"
+#include "P_SSLNextProtocolAccept.h"
+#include "P_SSLUtils.h"
+
+extern Map<int, SSLNextProtocolSet *> snpsMap;
+
+void
+SNIActionPerformer::PerformAction(Continuation *cont, cchar *servername)
+{
+  SNIConfig::scoped_config params;
+  auto actionvec = params->get(servername);
+  if (!actionvec)
+    Debug("ssl_sni", "%s not available in the map", servername);
+  else {
+    for (auto it : *actionvec) {
+      if (it)
+        it->SNIAction(cont);
+    }
+  }
+}
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index 5d2f20a..6d39a2d 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -304,25 +304,6 @@ SSLCertLookup::get(unsigned i) const
   return ssl_storage->get(i);
 }
 
-struct ats_wildcard_matcher {
-  ats_wildcard_matcher()
-  {
-    if (regex.compile(R"(^\*\.[^\*.]+)") != 0) {
-      Fatal("failed to compile TLS wildcard matching regex");
-    }
-  }
-
-  ~ats_wildcard_matcher() {}
-  bool
-  match(const char *hostname) const
-  {
-    return regex.match(hostname) != -1;
-  }
-
-private:
-  DFA regex;
-};
-
 static void
 make_to_lower_case(const char *name, char *lower_case_name, int buf_len)
 {
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index b18d302..a0da4fc 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -56,6 +56,7 @@ bool SSLConfigParams::session_cache_skip_on_lock_contention = false;
 size_t SSLConfigParams::session_cache_max_bucket_size       = 100;
 init_ssl_ctx_func SSLConfigParams::init_ssl_ctx_cb          = nullptr;
 load_ssl_file_func SSLConfigParams::load_ssl_file_cb        = nullptr;
+bool SSLConfigParams::sni_map_enable                        = false;
 
 // TS-3534 Wiretracing for SSL Connections
 int SSLConfigParams::ssl_wire_trace_enabled       = 0;
@@ -298,6 +299,7 @@ SSLConfigParams::initialize()
 
   // ++++++++++++++++++++++++ Client part ++++++++++++++++++++
   client_verify_depth = 7;
+  REC_EstablishStaticConfigByte(clientVerify, "proxy.config.ssl.client.verify.server");
 
   ssl_client_cert_filename = nullptr;
   ssl_client_cert_path     = nullptr;
@@ -321,6 +323,9 @@ SSLConfigParams::initialize()
   ats_free(clientCACertRelativePath);
   ats_free(ssl_client_ca_cert_filename);
 
+  // Enable/disable sni mapping
+  REC_ReadConfigInteger(sni_map_enable, "proxy.config.ssl.sni.map.enable");
+
   REC_ReadConfigInt32(ssl_allow_client_renegotiation, "proxy.config.ssl.allow_client_renegotiation");
 
   // SSL Wire Trace configurations
@@ -379,13 +384,12 @@ SSLConfigParams::InsertCTX(cchar *client_cert, SSL_CTX *cctx) const
 }
 
 void
-SSLConfigParams::printCTXmap()
+SSLConfigParams::printCTXmap() const
 {
   Vec<cchar *> keys;
   ctx_map.get_keys(keys);
-  for (size_t i = 0; i < keys.length(); i++) {
-    Debug("ssl", "Client certificates in the map %s", keys.get(i));
-  }
+  for (size_t i = 0; i < keys.length(); i++)
+    Debug("ssl", "Client certificates in the map %s: %p", keys.get(i), ctx_map.get(keys.get(i)));
 }
 void
 SSLConfigParams::freeCTXmap() const
@@ -404,7 +408,7 @@ SSLConfigParams::freeCTXmap() const
 }
 // creates a new context attaching the provided certificate
 SSL_CTX *
-SSLConfigParams::getNewCTX(char *client_cert) const
+SSLConfigParams::getNewCTX(cchar *client_cert) const
 {
   SSL_CTX *nclient_ctx = nullptr;
   nclient_ctx          = SSLInitClientContext(this);
diff --git a/iocore/net/SSLNetProcessor.cc b/iocore/net/SSLNetProcessor.cc
index c519691..b592581 100644
--- a/iocore/net/SSLNetProcessor.cc
+++ b/iocore/net/SSLNetProcessor.cc
@@ -26,6 +26,7 @@
 #include "I_RecHttp.h"
 #include "P_SSLUtils.h"
 #include "P_OCSPStapling.h"
+#include "P_SSLSNI.h"
 
 //
 // Global Data
@@ -33,7 +34,7 @@
 
 SSLNetProcessor ssl_NetProcessor;
 NetProcessor &sslNetProcessor = ssl_NetProcessor;
-
+SNIActionPerformer sni_action_performer;
 #ifdef HAVE_OPENSSL_OCSP_STAPLING
 struct OCSPContinuation : public Continuation {
   int
@@ -59,6 +60,7 @@ SSLNetProcessor::start(int, size_t stacksize)
   // This initialization order matters ...
   SSLInitializeLibrary();
   SSLConfig::startup();
+  SNIConfig::startup();
 
   if (!SSLCertificateConfig::startup()) {
     return -1;
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index fbb48c8..a0cf7b0 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -31,9 +31,12 @@
 #include "BIO_fastopen.h"
 #include "Log.h"
 #include "P_SSLClientUtils.h"
+#include "P_SSLSNI.h"
+#include "HttpTunnel.h"
 
 #include <climits>
 #include <string>
+#include <stdbool.h>
 
 #if !TS_USE_SET_RBIO
 // Defined in SSLInternal.c, should probably make a separate include
@@ -934,12 +937,19 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
       // Escape if this is marked to be a tunnel.
       // No data has been read at this point, so we can go
       // directly into blind tunnel mode
-      if (cc && SSLCertContext::OPT_TUNNEL == cc->opt && this->is_transparent) {
-        this->attributes     = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
-        sslHandShakeComplete = true;
-        SSL_free(this->ssl);
-        this->ssl = nullptr;
-        return EVENT_DONE;
+
+      if (cc && SSLCertContext::OPT_TUNNEL == cc->opt) {
+        if (this->is_transparent) {
+          this->attributes     = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+          sslHandShakeComplete = 1;
+          SSL_free(this->ssl);
+          this->ssl = NULL;
+          return EVENT_DONE;
+        } else {
+          SSLConfig::scoped_config params;
+          this->SNIMapping = params->sni_map_enable;
+          hookOpRequested  = SSL_HOOK_OP_TUNNEL;
+        }
       }
 
       // Attach the default SSL_CTX to this SSL session. The default context is never going to be able
@@ -965,40 +975,46 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
     return sslServerHandShakeEvent(err);
 
   case SSL_EVENT_CLIENT:
+
     if (this->ssl == nullptr) {
+      // Making the check here instead of later, so we only
+      // do this setting immediately after we create the SSL object
+      SNIConfig::scoped_config sniParam;
+      int8_t clientVerify = 0;
+      cchar *serverKey    = this->options.sni_servername;
+      if (!serverKey) {
+        char buff[INET6_ADDRSTRLEN];
+        ats_ip_ntop(this->get_remote_addr(), buff, INET6_ADDRSTRLEN);
+        serverKey = buff;
+      }
+      auto nps           = sniParam->getPropertyConfig(serverKey);
       SSL_CTX *clientCTX = nullptr;
-      if (this->options.clientCertificate) {
-        const char *certfile = (const char *)this->options.clientCertificate;
-        if (certfile != nullptr) {
-          clientCTX = params->getCTX(certfile);
-          if (clientCTX != nullptr) {
-            Debug("ssl", "context for %s is found at %p", this->options.clientCertificate.get(), (void *)clientCTX);
-          } else {
-            Debug("ssl", "failed to find context for %s", this->options.clientCertificate.get());
-          }
-        }
+
+      if (nps) {
+        clientCTX    = nps->ctx;
+        clientVerify = nps->verifyLevel;
       } else {
-        clientCTX = params->client_ctx;
+        clientCTX    = params->client_ctx;
+        clientVerify = params->clientVerify;
       }
-
-      if (this->options.clientVerificationFlag && params->clientCACertFilename != nullptr && params->clientCACertPath != nullptr) {
+      if (!clientCTX) {
+        SSLErrorVC(this, "failed to create SSL client session");
+        return EVENT_ERROR;
+      }
+      if (clientVerify && params->clientCACertFilename != nullptr && params->clientCACertPath != nullptr) {
         if (!SSL_CTX_load_verify_locations(clientCTX, params->clientCACertFilename, params->clientCACertPath)) {
           SSLError("invalid client CA Certificate file (%s) or CA Certificate path (%s)", params->clientCACertFilename,
                    params->clientCACertPath);
           return EVENT_ERROR;
         }
       }
-      this->ssl = make_ssl_connection(clientCTX, this);
-      if (this->ssl != nullptr) {
-        uint8_t clientVerify = this->options.clientVerificationFlag;
-        int verifyValue      = clientVerify & 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE;
-        SSL_set_verify(this->ssl, verifyValue, verify_callback);
-      }
 
+      this->ssl = make_ssl_connection(clientCTX, this);
       if (this->ssl == nullptr) {
         SSLErrorVC(this, "failed to create SSL client session");
         return EVENT_ERROR;
       }
+      SSL_set_verify(this->ssl, clientVerify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, verify_callback);
 
 #if TS_USE_TLS_SNI
       if (this->options.sni_servername) {
@@ -1050,7 +1066,8 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
   // Again no data has been exchanged, so we can go directly
   // without data replay.
   // Note we can't arrive here if a hook is active.
-  if (SSL_HOOK_OP_TUNNEL == hookOpRequested) {
+
+  if (SSL_HOOK_OP_TUNNEL == hookOpRequested && !SNIMapping) {
     this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
     SSL_free(this->ssl);
     this->ssl = nullptr;
@@ -1465,6 +1482,8 @@ SSLNetVConnection::sslContextSet(void *ctx)
   return zret;
 }
 
+extern TunnelHashMap TunnelMap; // stores the name of the servers to tunnel to
+
 bool
 SSLNetVConnection::callHooks(TSEvent eventId)
 {
@@ -1531,6 +1550,26 @@ SSLNetVConnection::callHooks(TSEvent eventId)
 
   Debug("ssl", "callHooks iterated to curHook=%p", curHook);
 
+  this->serverName = const_cast<char *>(SSL_get_servername(this->ssl, TLSEXT_NAMETYPE_host_name));
+  if (this->serverName) {
+    auto *hs = TunnelMap.find(this->serverName);
+    if (hs != nullptr) {
+      this->SNIMapping = true;
+      this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+      return EVENT_DONE;
+    }
+  }
+
+  if (SSL_HOOK_OP_TUNNEL == hookOpRequested && SNIMapping) {
+    this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+    // Don't mark the handshake as complete yet,
+    // Will be checking for that flag not being set after
+    // we get out of this callback, and then will shuffle
+    // over the buffered handshake packets to the O.S.
+    // sslHandShakeComplete = 1;
+    return EVENT_DONE;
+  }
+
   bool reenabled = true;
   if (curHook != nullptr) {
     curHook->invoke(eventId, this);
diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc
new file mode 100644
index 0000000..3b812de
--- /dev/null
+++ b/iocore/net/SSLSNIConfig.cc
@@ -0,0 +1,224 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ */
+
+/*************************** -*- Mod: C++ -*- ******************************
+  SSLSNIConfig.cc
+   Created On      : 05/02/2017
+
+   Description:
+   SNI based Configuration in ATS
+ ****************************************************************************/
+
+#include "P_SSLSNI.h"
+#include "ts/Diags.h"
+#include "ts/SimpleTokenizer.h"
+#include "P_SSLConfig.h"
+#include "ts/ink_memory.h"
+
+#define SNI_NAME_TAG "dest_host"
+#define SNI_ACTION_TAG "action"
+#define SNI_PARAM_TAG "param"
+
+static ConfigUpdateHandler<SNIConfig> *sniConfigUpdate;
+struct NetAccept;
+extern std::vector<NetAccept *> naVec;
+Map<int, SSLNextProtocolSet *> snpsMap;
+extern TunnelHashMap TunnelMap;
+NextHopProperty::NextHopProperty()
+{
+}
+
+NextHopProperty *
+SNIConfigParams::getPropertyConfig(cchar *servername) const
+{
+  NextHopProperty *nps = nullptr;
+  nps                  = next_hop_table.get(servername);
+  if (!nps)
+    nps = wild_next_hop_table.get(servername);
+  return nps;
+}
+
+void
+SNIConfigParams::loadSNIConfig()
+{
+  for (auto item : L_sni.items) {
+    actionVector *aiVec = new actionVector();
+    Debug("ssl", "name: %s", item.fqdn.data());
+    cchar *servername = item.fqdn.data();
+    ats_wildcard_matcher w_Matcher;
+    auto wildcard = w_Matcher.match(servername);
+
+    // set SNI based actions to be called in the ssl_servername_only callback
+    auto ai1 = new DisableH2();
+    aiVec->push_back(ai1);
+    auto ai2 = new VerifyClient(item.verify_client_level);
+    aiVec->push_back(ai2);
+    sni_action_map.put(ats_strdup(servername), aiVec);
+
+    if (item.tunnel_destination.length()) {
+      TunnelMap.emplace(item.fqdn.data(), item.tunnel_destination);
+    }
+
+    // set the next hop properties
+    SSLConfig::scoped_config params;
+    auto clientCTX  = params->getCTX(servername);
+    cchar *certFile = item.client_cert.data();
+    if (!clientCTX && certFile) {
+      clientCTX = params->getNewCTX(certFile);
+      params->InsertCTX(certFile, clientCTX);
+    }
+    NextHopProperty *nps = new NextHopProperty();
+    nps->name            = ats_strdup(servername);
+    nps->verifyLevel     = item.verify_origin_server;
+    nps->ctx             = clientCTX;
+    if (wildcard)
+      wild_next_hop_table.put(nps->name, nps);
+    else
+      next_hop_table.put(nps->name, nps);
+  } // end for
+}
+
+int SNIConfig::configid = 0;
+/*definition of member functions of SNIConfigParams*/
+SNIConfigParams::SNIConfigParams()
+{
+}
+
+actionVector *
+SNIConfigParams::get(cchar *servername) const
+{
+  auto actionVec = sni_action_map.get(servername);
+  if (!actionVec)
+    actionVec = wild_sni_action_map.get(servername);
+  return actionVec;
+}
+
+void
+SNIConfigParams::printSNImap() const
+{
+  Vec<cchar *> keys;
+  sni_action_map.get_keys(keys);
+  for (size_t i = 0; i < keys.length(); i++) {
+    Debug("ssl", "Domain name in the map %s: # of registered action items %lu", (char *)keys.get(i),
+          sni_action_map.get(keys.get(i))->size());
+  }
+}
+
+int
+SNIConfigParams::Initialize()
+{
+  sni_filename = ats_stringdup(RecConfigReadConfigPath("proxy.config.ssl.servername.filename"));
+  lua_State *L = lua_open(); /* opens Lua */
+  luaL_openlibs(L);
+  luaL_loadfile(L, sni_filename);
+  lua_pcall(L, 0, 0, 0);
+  L_sni.loader(L);
+  loadSNIConfig();
+  return 0;
+}
+
+void
+SNIConfigParams::cleanup()
+{
+  Vec<cchar *> keys;
+  sni_action_map.get_keys(keys);
+  for (int i = keys.length() - 1; i >= 0; i--) {
+    auto actionVec = sni_action_map.get(keys.get(i));
+    for (auto &ai : *actionVec)
+      delete ai;
+
+    actionVec->clear();
+  }
+  keys.free_and_clear();
+
+  wild_sni_action_map.get_keys(keys);
+  for (int i = keys.length() - 1; i >= 0; i--) {
+    auto actionVec = wild_sni_action_map.get(keys.get(i));
+    for (auto &ai : *actionVec)
+      delete ai;
+
+    actionVec->clear();
+  }
+  keys.free_and_clear();
+
+  next_hop_table.get_keys(keys);
+  for (int i = 0; i < static_cast<int>(keys.length()); i++) {
+    auto *nps = next_hop_table.get(keys.get(i));
+    delete (nps);
+  }
+  keys.free_and_clear();
+
+  wild_next_hop_table.get_keys(keys);
+  for (int i = 0; static_cast<int>(keys.length()); i++) {
+    auto *nps = wild_next_hop_table.get(keys.get(i));
+    delete (nps);
+  }
+  keys.free_and_clear();
+}
+
+SNIConfigParams::~SNIConfigParams()
+{
+  cleanup();
+}
+
+/*definition of member functions of SNIConfig*/
+void
+SNIConfig::startup()
+{
+  sniConfigUpdate = new ConfigUpdateHandler<SNIConfig>();
+  sniConfigUpdate->attach("proxy.config.ssl.servername.filename");
+  reconfigure();
+}
+
+void
+SNIConfig::cloneProtoSet()
+{
+  for (auto na : naVec) {
+    if (na->snpa) {
+      auto snps = na->snpa->cloneProtoSet();
+      snps->unregisterEndpoint(TS_ALPN_PROTOCOL_HTTP_2_0, nullptr);
+      snpsMap.put(na->id, snps);
+    }
+  }
+}
+
+void
+SNIConfig::reconfigure()
+{
+  SNIConfigParams *params = new SNIConfigParams;
+
+  params->Initialize();
+  configid = configProcessor.set(configid, params);
+}
+
+SNIConfigParams *
+SNIConfig::acquire()
+{
+  return (SNIConfigParams *)configProcessor.get(configid);
+}
+
+void
+SNIConfig::release(SNIConfigParams *params)
+{
+  configProcessor.release(configid, params);
+}
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index dffe016..37452e2 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -42,6 +42,7 @@
 #include <unistd.h>
 #include <termios.h>
 #include <vector>
+#include "P_SNIActionPerformer.h"
 
 #if HAVE_OPENSSL_EVP_H
 #include <openssl/evp.h>
@@ -68,6 +69,7 @@
 #define SSL_ACTION_TUNNEL_TAG "tunnel"
 #define SSL_SESSION_TICKET_ENABLED "ssl_ticket_enabled"
 #define SSL_KEY_DIALOG "ssl_key_dialog"
+#define SSL_SERVERNAME "dest_fqdn"
 #define SSL_CERT_SEPARATE_DELIM ','
 
 // openssl version must be 0.9.4 or greater
@@ -83,6 +85,7 @@
 #endif
 #endif
 
+TunnelHashMap TunnelMap; // stores the name of the servers to tunnel to
 /*
  * struct ssl_user_config: gather user provided settings from ssl_multicert.config in to this single struct
    * ssl_ticket_enabled - session ticket enabled
@@ -94,6 +97,7 @@
    * ssl_key_name - Private key
    * ticket_key_name - session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)]
    * ssl_key_dialog - Private key dialog
+   * servername - Destination server
    */
 struct ssl_user_config {
   ssl_user_config() : opt(SSLCertContext::OPT_NONE)
@@ -108,6 +112,7 @@ struct ssl_user_config {
   ats_scoped_str ca;
   ats_scoped_str key;
   ats_scoped_str dialog;
+  ats_scoped_str servername;
   SSLCertContext::Option opt;
 };
 
@@ -407,10 +412,16 @@ ssl_cert_callback(SSL *ssl, void * /*arg*/)
 /*
  * Cannot stop this callback. Always reeneabled
  */
+extern SNIActionPerformer sni_action_performer;
 static int
 ssl_servername_only_callback(SSL *ssl, int * /* ad */, void * /*arg*/)
 {
-  SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
+  SSLNetVConnection *netvc = reinterpret_cast<SSLNetVConnection *>(SSL_get_app_data(ssl));
+  const char *servername   = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  Debug("ssl", "Requested servername is %s", servername);
+  if (servername != nullptr) {
+    sni_action_performer.PerformAction(netvc, servername);
+  }
   netvc->callHooks(TS_EVENT_SSL_SERVERNAME);
   return SSL_TLSEXT_ERR_OK;
 }
@@ -1472,6 +1483,23 @@ ssl_set_handshake_callbacks(SSL_CTX *ctx)
 #endif
 }
 
+void
+setClientCertLevel(SSL *ssl, uint8_t certLevel)
+{
+  SSLConfig::scoped_config params;
+  int server_verify_client = SSL_VERIFY_NONE;
+
+  if (certLevel == 2) {
+    server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE;
+  } else if (certLevel == 1) {
+    server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+  }
+
+  Debug("ssl", "setting cert level to %d", server_verify_client);
+  SSL_set_verify(ssl, server_verify_client, nullptr);
+  SSL_set_verify_depth(ssl, params->verify_depth); // might want to make configurable at some point.
+}
+
 SSL_CTX *
 SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMultCertSettings, std::vector<X509 *> &certList)
 {
@@ -1946,6 +1974,11 @@ ssl_extract_certificate(const matcher_line *line_info, ssl_user_config &sslMultC
     if (strcasecmp(label, SSL_KEY_DIALOG) == 0) {
       sslMultCertSettings.dialog = ats_strdup(value);
     }
+
+    if (strcasecmp(label, SSL_SERVERNAME) == 0) {
+      sslMultCertSettings.servername = ats_strdup(value);
+    }
+
     if (strcasecmp(label, SSL_ACTION_TAG) == 0) {
       if (strcasecmp(SSL_ACTION_TUNNEL_TAG, value) == 0) {
         sslMultCertSettings.opt = SSLCertContext::OPT_TUNNEL;
@@ -1980,6 +2013,8 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
 
   Note("loading SSL certificate configuration from %s", params->configFilePath);
 
+  //  TunnelMap.clear();
+
   if (params->configFilePath) {
     file_buf = readIntoBuffer(params->configFilePath, __func__, nullptr);
   }
@@ -2009,7 +2044,7 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
       const char *errPtr;
 
       errPtr = parseConfigLine(line, &line_info, &sslCertTags);
-
+      Debug("ssl", "currently parsing %s", line);
       if (errPtr != nullptr) {
         RecSignalWarning(REC_SIGNAL_CONFIG_ERROR, "%s: discarding %s entry at line %d: %s", __func__, params->configFilePath,
                          line_num, errPtr);
diff --git a/lib/ts/Vec.h b/lib/ts/Vec.h
index f599ead..d3f6ac5 100644
--- a/lib/ts/Vec.h
+++ b/lib/ts/Vec.h
@@ -853,9 +853,8 @@ template <class C, class A, int S>
 inline void
 Vec<C, A, S>::free_and_clear()
 {
-  for (int x = 0; x < n; x++)
-    if (v[x])
-      A::free(v[x]);
+  for (size_t x = 0; x < (n); x++)
+    A::free((void *)v[x]);
   clear();
 }
 
diff --git a/lib/tsconfig/Makefile.am b/lib/tsconfig/Makefile.am
index 0e7584d..5d4216e 100644
--- a/lib/tsconfig/Makefile.am
+++ b/lib/tsconfig/Makefile.am
@@ -40,6 +40,8 @@ libtsconfig_la_SOURCES = \
   TsBuilder.h \
   TsConfigGrammar.hpp \
   TsConfigLexer.h \
+  TsConfigLua.cc \
+  TsConfigLua.h \
   TsConfigParseEvents.h \
   TsConfigTypes.h \
   TsErrataUtil.cc \
@@ -62,7 +64,7 @@ endif
 
 test_tsconfig_SOURCES = test-tsconfig.cc
 
-test_tsconfig_LDADD = libtsconfig.la ../ts/libtsutil.la
+test_tsconfig_LDADD = libtsconfig.la ../ts/libtsutil.la $(top_builddir)/lib/luajit/src/libluajit.a
 
 # Strip to just the enum in the ts::config namespace so we can use
 # it more easily in C++.
diff --git a/lib/tsconfig/TsConfigLua.cc b/lib/tsconfig/TsConfigLua.cc
new file mode 100644
index 0000000..46a2f54
--- /dev/null
+++ b/lib/tsconfig/TsConfigLua.cc
@@ -0,0 +1,53 @@
+/** @file
+
+    Implementation of the handler for parsing events.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+ */
+
+#include "TsConfigLua.h"
+
+ts::Errata TsConfigInt::loader(lua_State* s)
+{
+    ts::Errata zret;
+    ref = lua_tonumber(s,-1);
+    return zret;
+}
+
+ts::Errata TsConfigString::loader(lua_State* s)
+{
+    ts::Errata zret;
+    ref = lua_tostring(s,-1);
+    return zret;
+
+}
+
+ts::Errata TsConfigBool::loader(lua_State* s)
+{
+    ts::Errata zret;
+    ref = lua_toboolean(s,-1);
+    return zret;
+}
+
+//template <>
+//ts::Errata TsConfigEnum<Level>::loader(lua_State* s)
+//{
+//    ts::Errata zret;
+//    return zret;
+//}
diff --git a/lib/tsconfig/TsConfigLua.h b/lib/tsconfig/TsConfigLua.h
new file mode 100644
index 0000000..db79f99
--- /dev/null
+++ b/lib/tsconfig/TsConfigLua.h
@@ -0,0 +1,184 @@
+/** @file
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ */
+
+/*
+ * File:   TSConfigLua.h
+ * Author: persia
+ *
+ * Created on September 21, 2017, 4:04 PM
+ */
+
+
+#ifndef TSCONFIGLUA_H
+#define TSCONFIGLUA_H
+
+
+#include "tsconfig/Errata.h"
+#include <unordered_map>
+#include "luajit/src/lua.hpp"
+#include <iostream>
+#include <ts/string_view.h>
+#include <ts/HashFNV.h>
+
+/// Hash functor for @c string_view
+inline size_t TsLuaConfigSVHash(ts::string_view const& sv)
+{
+  ATSHash64FNV1a h;
+  h.update(sv.data(), sv.size());
+  return h.get();
+}
+
+/** Static schema data for a configuration value.
+
+    This is a base class for data about a configuration value. This is intended to be a singleton
+    static instance that contains schema data that is the same for all instances of the
+    configuration value.
+*/
+struct TsConfigDescriptor {
+  /// Type of the configuration value.
+  enum class Type {
+    ARRAY, ///< A homogenous array of nested values.
+    OBJECT, ///< A set of fields, each a name / value pair.
+    INT, ///< Integer value.
+    FLOAT, ///< Floating point value.
+    STRING, ///< String.
+    BOOL,
+    ENUM ///< Enumeration (specialized).
+  };
+/*  TsConfigDescriptor() : type_name(nullptr),name(nullptr),description(nullptr) {}
+  TsConfigDescriptor(Type typ,std::initializer_list<std::string> str_list): type(typ)
+  {
+      for (auto str :str_list) {
+                std::cout << str << std::endl;
+            }
+  }
+ * */
+  Type type; ///< Value type.
+  ts::string_view type_name; ///< Literal type name used in the schema.
+  ts::string_view name; ///< Name of the configuration value.
+  ts::string_view description; ///< Description of the  value.
+};
+
+/** Configuration item instance data.
+
+    This is an abstract base class for data about an instance of the value in a configuration
+    struct. Actual instances will be a subclass for a supported configuration item type. This holds
+    data that is per instance and therefore must be dynamically constructed as part of the
+    configuration struct construction. The related description classes in contrast are data that is
+    schema based and therefore can be static and shared among instances of the configuration struct.
+*/
+class TsConfigBase {
+public:
+  /// Source of the value in the config struct.
+  enum class Source {
+    NONE, ///< No source, the value is default constructed.
+    SCHEMA, ///< Value set in schema.
+    CONFIG ///< Value set in configuration file.
+  };
+  /// Constructor - need the static descriptor.
+  TsConfigBase(TsConfigDescriptor const& d) : descriptor(d) {}
+  TsConfigDescriptor const& descriptor; ///< Static schema data.
+  Source source = Source::NONE; ///< Where the instance data came from.
+  ~TsConfigBase()
+  {}
+  /// Load the instance data from the Lua stack.
+  virtual ts::Errata loader(lua_State* s) = 0;
+};
+
+class TsConfigInt : public TsConfigBase {
+public:
+   TsConfigInt(TsConfigDescriptor const& d, int& i):TsConfigBase(d),ref(i){}
+   ts::Errata loader(lua_State* s) override;
+private:
+   int & ref;
+};
+
+class TsConfigBool : public TsConfigBase {
+public:
+    TsConfigBool(TsConfigDescriptor const& d, bool& i):TsConfigBase(d), ref(i) {}
+    ts::Errata loader(lua_State* s) override;
+private:
+    bool &ref;
+
+};
+
+class TsConfigString : public TsConfigBase {
+public:
+   TsConfigString(TsConfigDescriptor const& d, std::string& str) : TsConfigBase(d), ref(str) {}
+//    TsConfigString& operator= (const TsConfigString& other)
+//    {
+//        ref = other.ref;
+//        return *this;
+//    }
+   ts::Errata loader(lua_State* s) override;
+private:
+   std::string& ref;
+};
+
+
+
+class TsConfigArrayDescriptor : public TsConfigDescriptor {
+public:
+   TsConfigArrayDescriptor(TsConfigDescriptor const& d) : item(d) {}
+   const TsConfigDescriptor& item;
+};
+
+class TsConfigEnumDescriptor : public TsConfigDescriptor {
+  using self_type = TsConfigEnumDescriptor;
+  using super_type = TsConfigDescriptor;
+public:
+  struct Pair { ts::string_view key; int value; };
+ TsConfigEnumDescriptor(Type t, ts::string_view t_name, ts::string_view n, ts::string_view d, std::initializer_list<Pair> pairs)
+    : super_type{t, t_name, n, d}, values{pairs.size(), &TsLuaConfigSVHash}, keys{pairs.size()}
+  {
+    for ( auto& p : pairs ) {
+      values[p.key] = p.value;
+      keys[p.value] = p.key;
+    }
+  }
+  std::unordered_map<ts::string_view, int, size_t(*)(ts::string_view const&) > values;
+  std::unordered_map<int, ts::string_view> keys;
+  int get(ts::string_view key)
+  {
+      return values[key];
+  }
+};
+
+class TsConfigObjectDescriptor : public TsConfigDescriptor {
+   std::unordered_map<std::string, TsConfigDescriptor const*> fields;
+};
+
+template < typename E >
+class TsConfigEnum : public TsConfigBase {
+public:
+   TsConfigEnum(TsConfigEnumDescriptor const& d, int& i) : TsConfigBase(d),edescriptor(d), ref(i) {}
+   TsConfigEnumDescriptor edescriptor;
+   int& ref;
+   ts::Errata loader(lua_State* L) override
+   {
+    ts::Errata zret;
+    std::string key(lua_tostring(L,-1));
+    ref = edescriptor.get(ts::string_view(key));
+    return zret;
+    }
+};
+
+#endif /* TSCONFIGLUA_H */
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 17b0672..fdc5b8a 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1146,6 +1146,8 @@ static const RecordElement RecordsConfig[] =
   {RECT_CONFIG, "proxy.config.ssl.server.multicert.filename", RECD_STRING, "ssl_multicert.config", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
   ,
   {RECT_CONFIG, "proxy.config.ssl.server.multicert.exit_on_load_fail", RECD_INT, "1", RECU_RESTART_TS, RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
+,
+  {RECT_CONFIG, "proxy.config.ssl.servername.filename", RECD_STRING, "ssl_server_name.config", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
   ,
   {RECT_CONFIG, "proxy.config.ssl.server.ticket_key.filename", RECD_STRING, nullptr, RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
   ,
@@ -1193,6 +1195,8 @@ static const RecordElement RecordsConfig[] =
   ,
   {RECT_CONFIG, "proxy.config.ssl.handshake_timeout_in", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_INT, "[0-65535]", RECA_NULL}
   ,
+  {RECT_CONFIG, "proxy.config.ssl.sni.map.enable", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
+  ,
   {RECT_CONFIG, "proxy.config.ssl.wire_trace_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-2]", RECA_NULL}
   ,
   {RECT_CONFIG, "proxy.config.ssl.wire_trace_addr", RECD_STRING, nullptr , RECU_DYNAMIC, RR_NULL, RECC_IP, R"([0-255]\.[0-255]\.[0-255]\.[0-255])", RECA_NULL}
diff --git a/mgmt/utils/MgmtUtils.h b/mgmt/utils/MgmtUtils.h
index 03acab6..633438a 100644
--- a/mgmt/utils/MgmtUtils.h
+++ b/mgmt/utils/MgmtUtils.h
@@ -39,6 +39,8 @@
 
 #include "P_RecCore.h"
 
+constexpr const char SSL_SERVER_NAME_CONFIG[] = "ssl_server_name.config";
+
 int mgmt_readline(int fd, char *buf, int maxlen);
 int mgmt_writeline(int fd, const char *data, int nbytes);
 
diff --git a/proxy/Main.cc b/proxy/Main.cc
index 48eaf64..5348726 100644
--- a/proxy/Main.cc
+++ b/proxy/Main.cc
@@ -92,6 +92,8 @@ extern "C" int plock(int);
 #include "I_Tasks.h"
 #include "InkAPIInternal.h"
 #include "HTTP2.h"
+#include "ts/ink_config.h"
+#include "P_SSLSNI.h"
 
 #include <ts/ink_cap.h>
 
@@ -1904,7 +1906,7 @@ main(int /* argc ATS_UNUSED */, const char **argv)
         start_HttpProxyServer(); // PORTS_READY_HOOK called from in here
       }
     }
-
+    SNIConfig::cloneProtoSet();
     // Plugins can register their own configuration names so now after they've done that
     // check for unexpected names. This is very late because remap plugins must be allowed to
     // fire up as well.
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index e031613..1df35af 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -206,6 +206,7 @@ traffic_server_LDADD += \
   $(top_builddir)/iocore/eventsystem/libinkevent.a \
   $(top_builddir)/lib/records/librecords_p.a \
   $(top_builddir)/iocore/eventsystem/libinkevent.a \
+  $(top_builddir)/lib/tsconfig/libtsconfig.la \
   @HWLOC_LIBS@ \
   @LIBPCRE@ \
   @LIBTCL@ \
diff --git a/proxy/config/Makefile.am b/proxy/config/Makefile.am
index c84c355..1206018 100644
--- a/proxy/config/Makefile.am
+++ b/proxy/config/Makefile.am
@@ -40,6 +40,7 @@ dist_sysconf_DATA =	\
   socks.config.default \
   splitdns.config.default \
   ssl_multicert.config.default \
+  ssl_server_name.config.default \
   volume.config.default
 
 install-exec-hook:
diff --git a/proxy/config/ssl_server_name.config.default b/proxy/config/ssl_server_name.config.default
new file mode 100644
index 0000000..2db9bbf
--- /dev/null
+++ b/proxy/config/ssl_server_name.config.default
@@ -0,0 +1,36 @@
+--[[
+ssl_server_name.config.default
+This configuration file
+    - sets the SSL actions to be performed based on the servername provided during SSL handhshake phase (SNI extension)
+    - sets the SSL properties required to make SSL connection with the next hop or origin server.
+
+Lua based Configuration file
+ Format : 
+ Actions available:
+   disable_h2 - removes H2 from the protocol list advertised by ATS; parameter required = None
+   verify_client - sets the verification flag for verifying the client certificate; parameters = Integer [0-2]
+   verify_origin_server - sets the verification flag for verifying the server certificate; parameters = Integer [0-2]
+   client_cert - sets the client certificate to present to the server specified in dest_host; parameters = certificate file .
+                     The location of the certificate file is relative to proxy.config.ssl.server.cert.path directory.
+   tunnel_route  - sets the e2e tunnel route
+ Example:
+
+
+params for client verification
+        NONE, MODERATE, STRICT
+
+server_config = {
+                {
+                    fqdn='one.com',
+                    disable_h2 = <true|false>,
+                    verify_origin_server = <NONE|MODERATE|STRICT>,
+                    client_cert = "somepem.pem",
+                    verify_client = <NONE|MODERATE|STRICT>
+                },
+                {
+                    fqdn='two.com',
+                    tunnel_route = 'two.com'
+                }
+};
+
+--]]
\ No newline at end of file
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index bb01166..d7c1ce8 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -42,6 +42,7 @@
 #include <openssl/ossl_typ.h>
 #include <openssl/ssl.h>
 #include "HttpPages.h"
+#include <unordered_map>
 
 #include "IPAllow.h"
 //#include "I_Auth.h"
@@ -69,6 +70,7 @@
 #define USE_NEW_EMPTY_MIOBUFFER
 
 extern int cache_config_read_while_writer;
+extern TunnelHashMap TunnelMap; // stores the name of the servers to tunnel to
 
 // We have a debugging list that can use to find stuck
 //  state machines
@@ -533,8 +535,39 @@ HttpSM::setup_client_read_request_header()
 void
 HttpSM::setup_blind_tunnel_port()
 {
-  // We gotten a connect on a port for blind tunneling so
-  //  call transact figure out where it is going
+  NetVConnection *netvc     = ua_session->get_netvc();
+  SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(netvc);
+  int host_len;
+  if (ssl_vc && ssl_vc->GetSNIMapping()) {
+    if (!t_state.hdr_info.client_request.url_get()->host_get(&host_len)) {
+      // the URL object has not been created in the start of the transaction. Hence, we need to create the URL here
+      URL u;
+
+      t_state.hdr_info.client_request.create(HTTP_TYPE_REQUEST);
+      t_state.hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
+      t_state.hdr_info.client_request.url_create(&u);
+      u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
+      t_state.hdr_info.client_request.url_set(&u);
+      auto *hs = TunnelMap.find(ssl_vc->serverName);
+      if (hs != nullptr) {
+        t_state.hdr_info.client_request.url_get()->host_set(hs->hostname, hs->len);
+        if (hs->port > 0) {
+          t_state.hdr_info.client_request.url_get()->port_set(hs->port);
+        } else {
+          t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+        }
+      } else {
+        t_state.hdr_info.client_request.url_get()->host_set(ssl_vc->serverName, strlen(ssl_vc->serverName));
+        t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+      }
+    }
+  } else {
+    char new_host[INET6_ADDRSTRLEN];
+    ats_ip_ntop(t_state.state_machine->ua_session->get_netvc()->get_local_addr(), new_host, sizeof(new_host));
+
+    t_state.hdr_info.client_request.url_get()->host_set(new_host, strlen(new_host));
+    t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+  }
   call_transact_and_set_next_state(HttpTransact::HandleBlindTunnel);
 }
 
@@ -1335,6 +1368,39 @@ HttpSM::state_api_callout(int event, void *data)
     pending_action = nullptr;
   // FALLTHROUGH
   case EVENT_NONE:
+    if (cur_hook_id == TS_HTTP_TXN_START_HOOK && t_state.client_info.port_attribute == HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
+      /* Creating the request object early to set the host header and port for blind tunneling here for the
+plugins required to work with sni_routing.
+*/
+      // Plugins triggered on txn_start_hook will get the host and port at that point
+      // We've received a request on a port which we blind forward
+      URL u;
+
+      t_state.hdr_info.client_request.create(HTTP_TYPE_REQUEST);
+      t_state.hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
+      t_state.hdr_info.client_request.url_create(&u);
+      u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
+      t_state.hdr_info.client_request.url_set(&u);
+
+      NetVConnection *netvc     = ua_session->get_netvc();
+      SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(netvc);
+      auto *hs                  = TunnelMap.find(ssl_vc->serverName);
+
+      if (ssl_vc && ssl_vc->GetSNIMapping()) {
+        if (hs != nullptr) {
+          t_state.hdr_info.client_request.url_get()->host_set(hs->hostname, hs->len);
+          if (hs->port > 0) {
+            t_state.hdr_info.client_request.url_get()->port_set(hs->port);
+          } else {
+            t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+          }
+        } else {
+          t_state.hdr_info.client_request.url_get()->host_set(ssl_vc->serverName, strlen(ssl_vc->serverName));
+          t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+        }
+      }
+    }
+  // FALLTHROUGH
   case HTTP_API_CONTINUE:
     if ((cur_hook_id >= 0) && (cur_hook_id < TS_HTTP_LAST_HOOK)) {
       if (!cur_hook) {
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 1053bec..54ba25f 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -542,34 +542,18 @@ HttpTransact::Forbidden(State *s)
 void
 HttpTransact::HandleBlindTunnel(State *s)
 {
-  NetVConnection *vc         = s->state_machine->ua_session->get_netvc();
-  bool inbound_transparent_p = vc->get_is_transparent();
   URL u;
   // IpEndpoint dest_addr;
   // ip_text_buffer new_host;
 
   DebugTxn("http_trans", "[HttpTransact::HandleBlindTunnel]");
 
-  // We've received a request on a port which we blind forward
-  //  For logging purposes we create a fake request
-  s->hdr_info.client_request.create(HTTP_TYPE_REQUEST);
-  s->hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
-  s->hdr_info.client_request.url_create(&u);
-  u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
-  s->hdr_info.client_request.url_set(&u);
-
   // We set the version to 0.9 because once we know where we are going
   //   this blind ssl tunnel is indistinguishable from a "CONNECT 0.9"
   //   except for the need to suppression error messages
   HTTPVersion ver(0, 9);
   s->hdr_info.client_request.version_set(ver);
 
-  char new_host[INET6_ADDRSTRLEN];
-  ats_ip_ntop(vc->get_local_addr(), new_host, sizeof(new_host));
-
-  s->hdr_info.client_request.url_get()->host_set(new_host, strlen(new_host));
-  s->hdr_info.client_request.url_get()->port_set(vc->get_local_port());
-
   // Initialize the state vars necessary to sending error responses
   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
 
@@ -579,35 +563,7 @@ HttpTransact::HandleBlindTunnel(State *s)
     DebugTxn("http_trans", "[HandleBlindTunnel] destination set to %.*s:%d", host_len, host,
              s->hdr_info.client_request.url_get()->port_get());
   }
-  // Now we need to run the url remapping engine to find the where
-  //   this request really goes since we were sent was bound for
-  //   machine we are running on
-
-  bool url_remap_success = false;
-  char *remap_redirect   = nullptr;
-
-  if (s->transparent_passthrough) {
-    url_remap_success = true;
-  }
-  // We must have mapping or we will self loop since the this
-  //    request was addressed to us to begin with.  Remap directs
-  //    are something used in the normal reverse proxy and if we
-  //    get them here they indicate a very bad misconfiguration!
-  if (!(inbound_transparent_p || url_remap_success) || remap_redirect != nullptr) {
-    // The error message we send back will be suppressed so
-    //  the only important thing in selecting the error is what
-    //  status code it gets logged as
-    build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Port Forwarding Error", "default");
 
-    int host_len;
-    const char *host = s->hdr_info.client_request.url_get()->host_get(&host_len);
-
-    Log::error("Forwarded port error: request with destination %.*s:%d "
-               "does not have a mapping",
-               host_len, host, s->hdr_info.client_request.url_get()->port_get());
-
-    TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
-  }
   // Set the mode to tunnel so that we don't lookup the cache
   s->current.mode = TUNNELLING_PROXY;
 
diff --git a/proxy/http/remap/RemapConfig.cc b/proxy/http/remap/RemapConfig.cc
index 1f889df..b32246f 100644
--- a/proxy/http/remap/RemapConfig.cc
+++ b/proxy/http/remap/RemapConfig.cc
@@ -1357,7 +1357,7 @@ remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti)
       ip_text_buffer ipb;   // buffer for address string conversion.
       if (0 == getaddrinfo(fromHost_lower, nullptr, nullptr, &ai_records)) {
         for (addrinfo *ai_spot = ai_records; ai_spot; ai_spot = ai_spot->ai_next) {
-          if (ats_is_ip(ai_spot->ai_addr) && !ats_is_ip_any(ai_spot->ai_addr)) {
+          if (ats_is_ip(ai_spot->ai_addr) && !ats_is_ip_any(ai_spot->ai_addr) && ai_spot->ai_protocol == IPPROTO_TCP) {
             url_mapping *u_mapping;
 
             ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb);
diff --git a/proxy/http/remap/RemapProcessor.cc b/proxy/http/remap/RemapProcessor.cc
index 767df83..8e76989 100644
--- a/proxy/http/remap/RemapProcessor.cc
+++ b/proxy/http/remap/RemapProcessor.cc
@@ -78,7 +78,6 @@ RemapProcessor::setup_for_remap(HttpTransact::State *s)
   request_host  = request_header->host_get(&request_host_len);
   request_port  = request_header->port_get();
   proxy_request = request_header->is_target_in_url() || !s->reverse_proxy;
-
   // Default to empty host.
   if (!request_host) {
     request_host     = "";
diff --git a/tests/gold_tests/autest-site/min_cfg/ssl_SNI.config b/tests/gold_tests/autest-site/min_cfg/ssl_SNI.config
new file mode 100644
index 0000000..e69de29
diff --git a/tests/gold_tests/autest-site/trafficserver.test.ext b/tests/gold_tests/autest-site/trafficserver.test.ext
index b1a4e4f..d678def 100755
--- a/tests/gold_tests/autest-site/trafficserver.test.ext
+++ b/tests/gold_tests/autest-site/trafficserver.test.ext
@@ -243,6 +243,10 @@ def MakeATSProcess(obj, name, command='traffic_server', select_ports=True):
     tmpname = os.path.join(config_dir, fname)
     p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
 
+    fname = "ssl_server_name.config"
+    tmpname = os.path.join(config_dir, fname)
+    p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
+
     fname = "storage.config"
     tmpname = os.path.join(config_dir, fname)
     p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].