tapv2: add option to set host-side default gw

Change-Id: I76fd655ecd9445299b94b3b5af10e7b1588584e4
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index 3654cd6..4f20ea8 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -7772,8 +7772,12 @@
   u8 host_mac_addr_set = 0;
   u8 *host_bridge = 0;
   ip4_address_t host_ip4_addr;
+  ip4_address_t host_ip4_gw;
+  u8 host_ip4_gw_set = 0;
   u32 host_ip4_prefix_len = 0;
   ip6_address_t host_ip6_addr;
+  ip6_address_t host_ip6_gw;
+  u8 host_ip6_gw_set = 0;
   u32 host_ip6_prefix_len = 0;
   int ret;
   int rx_ring_sz = 0, tx_ring_sz = 0;
@@ -7804,6 +7808,12 @@
       else if (unformat (i, "host-ip6-addr %U/%d", unformat_ip6_address,
 			 &host_ip6_addr, &host_ip6_prefix_len))
 	;
+      else if (unformat (i, "host-ip4-gw %U", unformat_ip4_address,
+			 &host_ip4_gw))
+	host_ip4_gw_set = 1;
+      else if (unformat (i, "host-ip6-gw %U", unformat_ip6_address,
+			 &host_ip6_gw))
+	host_ip6_gw_set = 1;
       else if (unformat (i, "rx-ring-size %d", &rx_ring_sz))
 	;
       else if (unformat (i, "tx-ring-size %d", &tx_ring_sz))
@@ -7885,7 +7895,10 @@
     clib_memcpy (mp->host_ip4_addr, &host_ip4_addr, 4);
   if (host_ip4_prefix_len)
     clib_memcpy (mp->host_ip6_addr, &host_ip6_addr, 16);
-
+  if (host_ip4_gw_set)
+    clib_memcpy (mp->host_ip4_gw, &host_ip4_gw, 4);
+  if (host_ip6_gw_set)
+    clib_memcpy (mp->host_ip6_gw, &host_ip6_gw, 16);
 
   vec_free (host_ns);
   vec_free (host_if_name);
diff --git a/src/vnet/devices/netlink.c b/src/vnet/devices/netlink.c
index 5994366..8346b1c 100644
--- a/src/vnet/devices/netlink.c
+++ b/src/vnet/devices/netlink.c
@@ -245,6 +245,48 @@
   return vnet_netlink_msg_send (&m);
 }
 
+clib_error_t *
+vnet_netlink_add_ip4_route (void *dst, u8 dst_len, void *gw)
+{
+  vnet_netlink_msg_t m;
+  struct rtmsg rtm = { 0 };
+  u8 dflt[4] = { 0 };
+
+  rtm.rtm_family = AF_INET;
+  rtm.rtm_table = RT_TABLE_MAIN;
+  rtm.rtm_type = RTN_UNICAST;
+  rtm.rtm_dst_len = dst_len;
+
+  vnet_netlink_msg_init (&m, RTM_NEWROUTE,
+			 NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
+			 &rtm, sizeof (struct rtmsg));
+
+  vnet_netlink_msg_add_rtattr (&m, RTA_GATEWAY, gw, 4);
+  vnet_netlink_msg_add_rtattr (&m, RTA_DST, dst ? dst : dflt, 4);
+  return vnet_netlink_msg_send (&m);
+}
+
+clib_error_t *
+vnet_netlink_add_ip6_route (void *dst, u8 dst_len, void *gw)
+{
+  vnet_netlink_msg_t m;
+  struct rtmsg rtm = { 0 };
+  u8 dflt[16] = { 0 };
+
+  rtm.rtm_family = AF_INET6;
+  rtm.rtm_table = RT_TABLE_MAIN;
+  rtm.rtm_type = RTN_UNICAST;
+  rtm.rtm_dst_len = dst_len;
+
+  vnet_netlink_msg_init (&m, RTM_NEWROUTE,
+			 NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
+			 &rtm, sizeof (struct rtmsg));
+
+  vnet_netlink_msg_add_rtattr (&m, RTA_GATEWAY, gw, 16);
+  vnet_netlink_msg_add_rtattr (&m, RTA_DST, dst ? dst : dflt, 16);
+  return vnet_netlink_msg_send (&m);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/vnet/devices/netlink.h b/src/vnet/devices/netlink.h
index e850196..0d3e0d2 100644
--- a/src/vnet/devices/netlink.h
+++ b/src/vnet/devices/netlink.h
@@ -26,6 +26,8 @@
 					 int pfx_len);
 clib_error_t *vnet_netlink_add_ip6_addr (int ifindex, void *addr,
 					 int pfx_len);
+clib_error_t *vnet_netlink_add_ip4_route (void *dst, u8 dst_len, void *gw);
+clib_error_t *vnet_netlink_add_ip6_route (void *dst, u8 dst_len, void *gw);
 
 #endif /* included_vnet_device_netlink_h */
 
diff --git a/src/vnet/devices/tap/cli.c b/src/vnet/devices/tap/cli.c
index 7dd525b..c17599a 100644
--- a/src/vnet/devices/tap/cli.c
+++ b/src/vnet/devices/tap/cli.c
@@ -61,10 +61,16 @@
 			     unformat_ip4_address, &args.host_ip4_addr,
 			     &args.host_ip4_prefix_len))
 	    ip_addr_set = 1;
+	  else if (unformat (line_input, "host-ip4-gw %U",
+			     unformat_ip4_address, &args.host_ip4_gw))
+	    args.host_ip4_gw_set = 1;
 	  else if (unformat (line_input, "host-ip6-addr %U/%d",
 			     unformat_ip6_address, &args.host_ip6_addr,
 			     &args.host_ip6_prefix_len))
 	    ip_addr_set = 1;
+	  else if (unformat (line_input, "host-ip6-gw %U",
+			     unformat_ip6_address, &args.host_ip6_gw))
+	    args.host_ip6_gw_set = 1;
 	  else if (unformat (line_input, "rx-ring-size %d", &args.rx_ring_sz))
 	    ;
 	  else if (unformat (line_input, "tx-ring-size %d", &args.tx_ring_sz))
@@ -102,7 +108,8 @@
   .short_help = "create tap {id <if-id>} [hw-addr <mac-address>] "
     "[rx-ring-size <size>] [tx-ring-size <size>] [host-ns <netns>] "
     "[host-bridge <bridge-name>] [host-ip4-addr <ip4addr/mask>] "
-    "[host-ip6-addr <ip6-addr] [host-if-name <name>]",
+    "[host-ip6-addr <ip6-addr>] [host-ip4-gw <ip4-addr>] "
+    "[host-ip6-gw <ip6-addr>] [host-if-name <name>]",
   .function = tap_create_command_fn,
 };
 /* *INDENT-ON* */
diff --git a/src/vnet/devices/tap/tap.c b/src/vnet/devices/tap/tap.c
index a7d10fe..e6a7ba5 100644
--- a/src/vnet/devices/tap/tap.c
+++ b/src/vnet/devices/tap/tap.c
@@ -292,6 +292,26 @@
       goto error;
     }
 
+  if (args->host_ip4_gw_set)
+    {
+      args->error = vnet_netlink_add_ip4_route (0, 0, &args->host_ip4_gw);
+      if (args->error)
+	{
+	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
+	  goto error;
+	}
+    }
+
+  if (args->host_ip6_gw_set)
+    {
+      args->error = vnet_netlink_add_ip6_route (0, 0, &args->host_ip6_gw);
+      if (args->error)
+	{
+	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
+	  goto error;
+	}
+    }
+
   /* switch back to old net namespace */
   if (args->host_namespace)
     {
diff --git a/src/vnet/devices/tap/tap.h b/src/vnet/devices/tap/tap.h
index 35f7a01..98af0d8 100644
--- a/src/vnet/devices/tap/tap.h
+++ b/src/vnet/devices/tap/tap.h
@@ -35,8 +35,12 @@
   u8 *host_bridge;
   ip4_address_t host_ip4_addr;
   u8 host_ip4_prefix_len;
+  ip4_address_t host_ip4_gw;
+  u8 host_ip4_gw_set;
   ip6_address_t host_ip6_addr;
   u8 host_ip6_prefix_len;
+  ip6_address_t host_ip6_gw;
+  u8 host_ip6_gw_set;
   /* return */
   u32 sw_if_index;
   int rv;
diff --git a/src/vnet/devices/tap/tapv2.api b/src/vnet/devices/tap/tapv2.api
index 1f0051e..0d86edf 100644
--- a/src/vnet/devices/tap/tapv2.api
+++ b/src/vnet/devices/tap/tapv2.api
@@ -19,7 +19,7 @@
     the Linux kernel TAP device driver
 */
 
-vl_api_version 1.0.0
+vl_api_version 1.1.0
 
 /** \brief Initialize a new tap interface with the given paramters
     @param client_index - opaque cookie to identify the sender
@@ -43,6 +43,10 @@
     @param host_ip6_addr_set - host IPv6 ip address should be set
     @param host_ip6_addr - host IPv6 ip address
     @param host_ip6_prefix_len - host IPv6 ip address prefix length
+    @param host_ip4_gw_set - host IPv4 default gateway should be set
+    @param host_ip4_gw - host IPv4 default gateway
+    @param host_ip6_gw_set - host IPv6 default gateway should be set
+    @param host_ip6_gw - host IPv6 default gateway
 */
 define tap_create_v2
 {
@@ -67,6 +71,10 @@
   u8 host_ip6_addr_set;
   u8 host_ip6_addr[16];
   u8 host_ip6_prefix_len;
+  u8 host_ip4_gw_set;
+  u8 host_ip4_gw[4];
+  u8 host_ip6_gw_set;
+  u8 host_ip6_gw[16];
 };
 
 /** \brief Reply for tap create reply
diff --git a/src/vnet/devices/tap/tapv2_api.c b/src/vnet/devices/tap/tapv2_api.c
index 9f46884..34472c0 100644
--- a/src/vnet/devices/tap/tapv2_api.c
+++ b/src/vnet/devices/tap/tapv2_api.c
@@ -96,6 +96,18 @@
       ap->host_ip6_prefix_len = mp->host_ip6_prefix_len;
     }
 
+  if (mp->host_ip4_gw_set)
+    {
+      clib_memcpy (&ap->host_ip4_gw, mp->host_ip4_gw, 4);
+      ap->host_ip4_gw_set = 1;
+    }
+
+  if (mp->host_ip6_gw_set)
+    {
+      clib_memcpy (&ap->host_ip6_gw, mp->host_ip6_gw, 16);
+      ap->host_ip6_gw_set = 1;
+    }
+
   tap_create_if (vm, ap);
 
   reg = vl_api_client_index_to_registration (mp->client_index);
diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c
index 2af3b47..62f3c5f 100644
--- a/src/vpp/api/custom_dump.c
+++ b/src/vpp/api/custom_dump.c
@@ -576,6 +576,10 @@
   if (mp->host_ip6_addr_set)
     s = format (s, "host-ip6-addr %U/%d ", format_ip6_address,
 		mp->host_ip6_addr, mp->host_ip6_prefix_len);
+  if (mp->host_ip4_gw_set)
+    s = format (s, "host-ip4-gw %U ", format_ip4_address, mp->host_ip4_addr);
+  if (mp->host_ip6_gw_set)
+    s = format (s, "host-ip6-gw %U ", format_ip6_address, mp->host_ip6_addr);
   if (mp->tx_ring_sz)
     s = format (s, "tx-ring-size %d ", mp->tx_ring_sz);
   if (mp->rx_ring_sz)