From 1a3aca0a849af0bae994c9cf89de0dcfe7b310c2 Mon Sep 17 00:00:00 2001
From: Micael Karlberg <bmk@erlang.org>
Date: Wed, 18 Jul 2018 12:05:00 +0200
Subject: [socket-nif] Add support for socket (level ipv6) option mtu_discover

Added support for the VPv6 socket option MTU_DISCOVER.

OTP-14831.
---
 erts/doc/src/socket_usage.xml          |  21 ++--
 erts/emulator/nifs/common/socket_nif.c | 190 +++++++++++++++++++++++++++++++++
 erts/preloaded/ebin/socket.beam        | Bin 51480 -> 51660 bytes
 erts/preloaded/src/socket.erl          |  16 ++-
 4 files changed, 217 insertions(+), 10 deletions(-)

(limited to 'erts')

diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index cdd98090e8..247cd0eccb 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -145,7 +145,7 @@
 	<cell>none</cell>
       </row>
       <row>
-	<cell>peep_off</cell>
+	<cell>peek_off</cell>
 	<cell>integer()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
@@ -255,7 +255,7 @@
       </row>
       <row>
 	<cell>mtu_discover</cell>
-	<cell>ip_mtu_discover()</cell>
+	<cell>ip_pmtudisc()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
 	<cell>none</cell>
@@ -376,6 +376,13 @@
 	<cell>yes</cell>
 	<cell>Get: Only after the socket has been connected</cell>
       </row>
+      <row>
+	<cell>mtu_discover</cell>
+	<cell>ipv6_pmtudisc()</cell>
+	<cell>yes</cell>
+	<cell>yes</cell>
+	<cell>none</cell>
+      </row>
       <row>
 	<cell>v6only</cell>
 	<cell>boolean()</cell>
@@ -406,14 +413,14 @@
 	<cell>integer()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
-	<cell>type = stream, protocol = tcp</cell>
+	<cell>none</cell>
       </row>
       <row>
 	<cell>nodelay</cell>
 	<cell>boolean()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
-	<cell>type = stream, protocol = tcp</cell>
+	<cell>none</cell>
       </row>
     </table>
     
@@ -431,7 +438,7 @@
 	<cell>boolean()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
-	<cell>type = dgram, protocol = udp</cell>
+	<cell>none</cell>
       </row>
     </table>
     
@@ -449,14 +456,14 @@
 	<cell>integer()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
-	<cell>protocol = sctp</cell>
+	<cell>none</cell>
       </row>
       <row>
 	<cell>nodelay</cell>
 	<cell>boolean()</cell>
 	<cell>yes</cell>
 	<cell>yes</cell>
-	<cell>protocol = sctp</cell>
+	<cell>none</cell>
       </row>
     </table>
     
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 0222d58e6d..d2f7e21ad0 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -389,6 +389,7 @@ typedef union {
 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP       6
 #define SOCKET_OPT_IPV6_HOPLIMIT             12
 #define SOCKET_OPT_IPV6_MTU                  17
+#define SOCKET_OPT_IPV6_MTU_DISCOVER         18
 #define SOCKET_OPT_IPV6_V6ONLY               33
 
 #define SOCKET_OPT_TCP_CONGESTION   1
@@ -1014,6 +1015,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv*        env,
                                          SocketDescriptor* descP,
                                          ERL_NIF_TERM      eVal);
 #endif
+#if defined(IPV6_MTU_DISCOVER)
+static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv*        env,
+                                                  SocketDescriptor* descP,
+                                                  ERL_NIF_TERM      eVal);
+#endif
 #if defined(IPV6_V6ONLY)
 static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv*        env,
                                             SocketDescriptor* descP,
@@ -1237,6 +1243,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv*        env,
 static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv*        env,
                                          SocketDescriptor* descP);
 #endif
+#if defined(IPV6_MTU_DISCOVER)
+static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv*        env,
+                                                  SocketDescriptor* descP);
+#endif
 #if defined(IPV6_V6ONLY)
 static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv*        env,
                                             SocketDescriptor* descP);
@@ -1451,6 +1461,16 @@ static void encode_ip_pmtudisc(ErlNifEnv*    env,
                                int           val,
                                ERL_NIF_TERM* eVal);
 #endif
+#if defined(IPV6_MTU_DISCOVER)
+static char* decode_ipv6_pmtudisc(ErlNifEnv*   env,
+                                  ERL_NIF_TERM eVal,
+                                  int*         val);
+#endif
+#if defined(IPV6_MTU_DISCOVER)
+static void encode_ipv6_pmtudisc(ErlNifEnv*    env,
+                                 int           val,
+                                 ERL_NIF_TERM* eVal);
+#endif
 
 /*
 static BOOLEAN_T decode_bool(ErlNifEnv*   env,
@@ -4977,6 +4997,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv*        env,
         break;
 #endif
 
+#if defined(IPV6_MTU_DISCOVER)
+    case SOCKET_OPT_IPV6_MTU_DISCOVER:
+        result = nsetopt_lvl_ipv6_mtu_discover(env, descP, eVal);
+        break;
+#endif
+
 #if defined(IPV6_V6ONLY)
     case SOCKET_OPT_IPV6_V6ONLY:
         result = nsetopt_lvl_ipv6_v6only(env, descP, eVal);
@@ -5038,6 +5064,42 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv*        env,
 #endif
 
 
+/* nsetopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
+ *
+ * The value is an atom of the type ipv6_pmtudisc().
+ */
+#if defined(IPV6_MTU_DISCOVER)
+static
+ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv*        env,
+                                           SocketDescriptor* descP,
+                                           ERL_NIF_TERM      eVal)
+{
+    ERL_NIF_TERM  result;
+    int           val;
+    char*         xres;
+    int           res;
+
+    if ((xres = decode_ipv6_pmtudisc(env, eVal, &val)) != NULL) {
+
+        result = esock_make_error_str(env, xres);
+
+    } else {
+
+        res = socket_setopt(descP->sock, SOL_IPV6, IPV6_MTU_DISCOVER,
+                            &val, sizeof(val));
+
+        if (res != 0)
+            result = esock_make_error_errno(env, sock_errno());
+        else
+            result = esock_atom_ok;
+
+    }
+
+    return result;
+}
+#endif
+
+
 #if defined(IPV6_V6ONLY)
 static
 ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv*        env,
@@ -6731,6 +6793,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv*        env,
         break;
 #endif
 
+#if defined(IPV6_MTU_DISCOVER)
+    case SOCKET_OPT_IPV6_MTU_DISCOVER:
+        result = ngetopt_lvl_ipv6_mtu_discover(env, descP);
+        break;
+#endif
+
 #if defined(IPV6_V6ONLY)
     case SOCKET_OPT_IPV6_V6ONLY:
         result = ngetopt_lvl_ipv6_v6only(env, descP);
@@ -6771,6 +6839,35 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv*        env,
 #endif
 
 
+/* ngetopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
+ */
+#if defined(IPV6_MTU_DISCOVER)
+static
+ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv*        env,
+                                           SocketDescriptor* descP)
+{
+    ERL_NIF_TERM  result;
+    ERL_NIF_TERM  eMtuDisc;
+    int           mtuDisc;
+    SOCKOPTLEN_T  mtuDiscSz = sizeof(mtuDisc);
+    int           res;
+
+    res = sock_getopt(descP->sock, SOL_IPV6, IPV6_MTU_DISCOVER,
+                      &mtuDisc, &mtuDiscSz);
+
+    if (res != 0) {
+        result = esock_make_error_errno(env, sock_errno());
+    } else {
+        encode_ipv6_pmtudisc(env, mtuDisc, &eMtuDisc);
+        result = esock_make_ok2(env, eMtuDisc);
+    }
+
+    return result;
+
+}
+#endif
+
+
 #if defined(IPV6_V6ONLY)
 static
 ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv*        env,
@@ -7723,6 +7820,57 @@ char* decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
 
 
 
+/* +++ decode the ipv6 socket option MTU_DISCOVER +++
+ * The (ip) option can be provide in two ways:
+ *
+ *           atom() | integer()
+ *
+ * When its an atom it can have the values:
+ *
+ *       want | dont | do | probe
+ *
+ */
+#if defined(IPV6_MTU_DISCOVER)
+static
+char* decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
+{
+    char* res = NULL;
+
+    if (IS_ATOM(env, eVal)) {
+
+        if (COMPARE(eVal, atom_want) == 0) {
+            *val = IPV6_PMTUDISC_WANT;
+        } else if (COMPARE(eVal, atom_dont) == 0) {
+            *val = IPV6_PMTUDISC_DONT;
+        } else if (COMPARE(eVal, atom_do) == 0) {
+            *val = IPV6_PMTUDISC_DO;
+        } else if (COMPARE(eVal, atom_probe) == 0) {
+            *val = IPV6_PMTUDISC_PROBE;
+        } else {
+            *val = -1;
+            res  = ESOCK_STR_EINVAL;
+        }
+
+    } else if (IS_NUM(env, eVal)) {
+
+        if (!GET_INT(env, eVal, val)) {
+            *val = -1;
+            res  = ESOCK_STR_EINVAL;
+        }
+
+    } else {
+
+        *val   = -1;
+        res  = ESOCK_STR_EINVAL;
+
+    }
+
+    return res;
+}
+#endif
+
+
+
 /* +++ encode the ip socket option MTU_DISCOVER +++
  * The (ip) option can be provide in two ways:
  *
@@ -7765,6 +7913,48 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
 
 
 
+/* +++ encode the ipv6 socket option MTU_DISCOVER +++
+ * The (ipv6) option can be provide in two ways:
+ *
+ *           atom() | integer()
+ *
+ * If its one of the "known" values, it will be an atom:
+ *
+ *       want | dont | do | probe
+ *
+ */
+#if defined(IPV6_MTU_DISCOVER)
+static
+void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
+{
+    switch (val) {
+    case IPV6_PMTUDISC_WANT:
+        *eVal = atom_want;
+        break;
+
+    case IPV6_PMTUDISC_DONT:
+        *eVal = atom_dont;
+        break;
+
+    case IPV6_PMTUDISC_DO:
+        *eVal = atom_do;
+        break;
+
+    case IPV6_PMTUDISC_PROBE:
+        *eVal = atom_probe;
+        break;
+
+    default:
+        *eVal = MKI(env, val);
+        break;
+    }
+
+    return;
+}
+#endif
+
+
+
 /* +++ decocde the native getopt option +++
  * The option is in this case provide in the form of a two tuple:
  *
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 7ab8b0b3c4..cb1c5fc815 100644
Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 41df672ef5..ead058c607 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -93,6 +93,7 @@
               ip_mreq_source/0,
               ip_pmtudisc/0,
               ipv6_mreq/0,
+              ipv6_pmtudisc/0,
 
 
               msg_hdr/0
@@ -164,6 +165,8 @@
 -type ipv6_mreq() :: #{multiaddr := ip6_address(),
                        interface := non_neg_integer()}.
 
+-type ipv6_pmtudisc() :: ip_pmtudisc().
+
 -type sockaddr_un()  :: #{family := local,
                           path   := binary() | string()}.
 -type sockaddr_in4() :: #{family := inet,
@@ -587,7 +590,7 @@
 %% -define(SOCKET_OPT_IPV6_JOIN_GROUP,        15).
 %% -define(SOCKET_OPT_IPV6_LEAVE_GROUP,       16).
 -define(SOCKET_OPT_IPV6_MTU,               17).
-%% -define(SOCKET_OPT_IPV6_MTU_DISCOVER,      18).
+-define(SOCKET_OPT_IPV6_MTU_DISCOVER,      18).
 %% -define(SOCKET_OPT_IPV6_MULTICAST_HOPS,    19).
 %% -define(SOCKET_OPT_IPV6_MULTICAST_IF,      20).
 %% -define(SOCKET_OPT_IPV6_MULTICAST_LOOP,    21).
@@ -2107,6 +2110,13 @@ enc_setopt_value(ipv6, hoplimit, V, _D, T, _P)
     V;
 enc_setopt_value(ipv6, mtu, V, _D, _T, _P) when is_integer(V) ->
     V;
+enc_setopt_value(ipv6, mtu_discover, V, _D, _T, _P)
+  when (V =:= want)  orelse
+       (V =:= dont)  orelse
+       (V =:= do)    orelse
+       (V =:= probe) orelse
+       is_integer(V) ->
+    V;
 enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) ->
     V;
 enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) ->
@@ -2462,8 +2472,8 @@ enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) ->
     not_supported({L, Opt});
 enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) ->
     ?SOCKET_OPT_IPV6_MTU;
-enc_sockopt_key(ipv6 = L, mtu_discover = Opt, _Dir, _D, _T, _P) ->
-    not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
+    ?SOCKET_OPT_IPV6_MTU_DISCOVER;
 enc_sockopt_key(ipv6 = L, multicast_hops = Opt, _Dir, _D, _T, _P) ->
     not_supported({L, Opt});
 enc_sockopt_key(ipv6 = L, multicast_if = Opt, _Dir, _D, T, _P) 
-- 
cgit v1.2.3