From 7d5b6e7bf640eb5d64679e3bf7b440b8e21e3a4d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 15:03:36 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option router_alert Added support for the IPv6 socket option ROUTER_ALERT. Only supported for raw sockets. OTP-14831. --- erts/doc/src/socket_usage.xml | 7 ++ erts/emulator/nifs/common/socket_nif.c | 138 +++++++++++++++++++++++++++------ erts/preloaded/ebin/socket.beam | Bin 57664 -> 57952 bytes erts/preloaded/src/socket.erl | 25 ++++-- 4 files changed, 140 insertions(+), 30 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml index 383aebdab1..1423571f5c 100644 --- a/erts/doc/src/socket_usage.xml +++ b/erts/doc/src/socket_usage.xml @@ -502,6 +502,13 @@ yes type = dgram | raw + + router_alert + integer() + yes + yes + type = raw + rthdr boolean() diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5aba0bca03..4d78e89a4f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -470,6 +470,8 @@ typedef union { #define SOCKET_PROTOCOL_TCP 2 #define SOCKET_PROTOCOL_UDP 3 #define SOCKET_PROTOCOL_SCTP 4 +#define SOCKET_PROTOCOL_ICMP 5 +#define SOCKET_PROTOCOL_IGMP 6 /* shutdown how */ #define SOCKET_SHUTDOWN_HOW_RD 0 @@ -548,6 +550,7 @@ typedef union { #define SOCKET_OPT_IPV6_MULTICAST_IF 20 #define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 #define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD +#define SOCKET_OPT_IPV6_ROUTER_ALERT 27 #define SOCKET_OPT_IPV6_RTHDR 28 #define SOCKET_OPT_IPV6_UNICAST_HOPS 30 #define SOCKET_OPT_IPV6_V6ONLY 32 @@ -1266,6 +1269,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_ROUTER_ALERT) +static ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_RTHDR) static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP, @@ -1597,6 +1605,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_ROUTER_ALERT) +static ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_RTHDR) static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP); @@ -1896,7 +1908,9 @@ static int compare_pids(ErlNifEnv* env, static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); -static BOOLEAN_T eproto2proto(int eproto, int* proto); +static BOOLEAN_T eproto2proto(ErlNifEnv* env, + const ERL_NIF_TERM eproto, + int* proto); static BOOLEAN_T ehow2how(unsigned int ehow, int* how); static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags); static BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags); @@ -2344,18 +2358,18 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, if ((argc != 4) || !GET_INT(env, argv[0], &edomain) || !GET_INT(env, argv[1], &etype) || - !GET_INT(env, argv[2], &eproto) || !IS_MAP(env, argv[3])) { return enif_make_badarg(env); } - emap = argv[3]; + eproto = argv[2]; + emap = argv[3]; SGDBG( ("SOCKET", "nif_open -> " "\r\n edomain: %T" "\r\n etype: %T" "\r\n eproto: %T" "\r\n extra: %T" - "\r\n", argv[0], argv[1], argv[2], argv[3]) ); + "\r\n", argv[0], argv[1], eproto, emap) ); if (!edomain2domain(edomain, &domain)) { SGDBG( ("SOCKET", "nif_open -> domain: %d\r\n", domain) ); @@ -2367,7 +2381,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); } - if (!eproto2proto(eproto, &proto)) { + if (!eproto2proto(env, eproto, &proto)) { SGDBG( ("SOCKET", "nif_open -> protocol: %d\r\n", proto) ); return esock_make_error(env, esock_atom_einval); } @@ -5671,6 +5685,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_ROUTER_ALERT) + case SOCKET_OPT_IPV6_ROUTER_ALERT: + result = nsetopt_lvl_ipv6_router_alert(env, descP, eVal); + break; +#endif + #if defined(IPV6_RTHDR) case SOCKET_OPT_IPV6_RTHDR: result = nsetopt_lvl_ipv6_rthdr(env, descP, eVal); @@ -5883,6 +5903,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif +#if defined(IPV6_ROUTER_ALERT) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_ROUTER_ALERT, eVal); +} +#endif + + + #if defined(IPV6_RTHDR) static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, @@ -8299,6 +8331,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_ROUTER_ALERT) + case SOCKET_OPT_IPV6_ROUTER_ALERT: + result = ngetopt_lvl_ipv6_router_alert(env, descP); + break; +#endif + #if defined(IPV6_RTHDR) case SOCKET_OPT_IPV6_RTHDR: result = ngetopt_lvl_ipv6_rthdr(env, descP); @@ -8466,6 +8504,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif +#if defined(IPV6_ROUTER_ALERT) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_ROUTER_ALERT); +} +#endif + + #if defined(IPV6_RTHDR) static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, @@ -8479,7 +8527,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, #if defined(IPV6_UNICAST_HOPS) static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, - SocketDescriptor* descP) + SocketDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_UNICAST_HOPS); } @@ -10235,30 +10283,72 @@ BOOLEAN_T etype2type(int etype, int* type) * Note that only a subset is supported. */ static -BOOLEAN_T eproto2proto(int eproto, int* proto) +BOOLEAN_T eproto2proto(ErlNifEnv* env, + ERL_NIF_TERM eproto, + int* proto) { - switch (eproto) { - case SOCKET_PROTOCOL_IP: - *proto = IPPROTO_IP; - break; - - case SOCKET_PROTOCOL_TCP: - *proto = IPPROTO_TCP; - break; + if (IS_NUM(env, eproto)) { + int ep; - case SOCKET_PROTOCOL_UDP: - *proto = IPPROTO_UDP; - break; + if (!GET_INT(env, eproto, &ep)) { + *proto = -1; + return FALSE; + } + switch (ep) { + case SOCKET_PROTOCOL_IP: + *proto = IPPROTO_IP; + break; + + case SOCKET_PROTOCOL_TCP: + *proto = IPPROTO_TCP; + break; + + case SOCKET_PROTOCOL_UDP: + *proto = IPPROTO_UDP; + break; + #if defined(HAVE_SCTP) - case SOCKET_PROTOCOL_SCTP: - *proto = IPPROTO_SCTP; - break; + case SOCKET_PROTOCOL_SCTP: + *proto = IPPROTO_SCTP; + break; #endif + + case SOCKET_PROTOCOL_ICMP: + *proto = IPPROTO_ICMP; + break; + + case SOCKET_PROTOCOL_IGMP: + *proto = IPPROTO_IGMP; + break; + + default: + *proto = -2; + return FALSE; + } + } else { + const ERL_NIF_TERM* a; + int sz; - default: - *proto = -1; - return FALSE; + if (!GET_TUPLE(env, eproto, &sz, &a)) { + *proto = -3; + return FALSE; + } + + if (sz != 2) { + *proto = -4; + return FALSE; + } + + if (COMPARE(a[0], esock_atom_raw) != 0) { + *proto = -5; + return FALSE; + } + + if (!GET_INT(env, a[1], proto)) { + *proto = -6; + return FALSE; + } } return TRUE; diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam index 5ab64f442f..cdc403055b 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 3bc3c7dc28..82a51ee245 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -128,7 +128,10 @@ -type type() :: stream | dgram | raw | rdm | seqpacket. %% We support only a subset of all protocols: --type protocol() :: ip | tcp | udp | sctp. +%% Note that the '{raw, integer()}' construct is intended +%% to be used with type = raw. +%% Note also that only the "superuser" can create a raw socket. +-type protocol() :: ip | tcp | udp | sctp | icmp | igmp | {raw, integer()}. -type port_number() :: 0..65535. @@ -525,6 +528,8 @@ -define(SOCKET_PROTOCOL_TCP, 2). -define(SOCKET_PROTOCOL_UDP, 3). -define(SOCKET_PROTOCOL_SCTP, 4). +-define(SOCKET_PROTOCOL_ICMP, 5). +-define(SOCKET_PROTOCOL_IGMP, 6). -define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). @@ -656,7 +661,7 @@ %% -define(SOCKET_OPT_IPV6_RECVERR, 24). -define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO %% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26). -%% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). +-define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). -define(SOCKET_OPT_IPV6_RTHDR, 28). %% -define(SOCKET_OPT_IPV6_TCLASS, 29). -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). @@ -2004,7 +2009,8 @@ enc_type(_, raw) -> ?SOCKET_TYPE_RAW; enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET; enc_type(_, Type) -> throw({error, {invalid_type, Type}}). --spec enc_protocol(Type, Protocol) -> non_neg_integer() when +-spec enc_protocol(Type, Protocol) -> non_neg_integer() | + {raw, non_neg_integer()} when Type :: type(), Protocol :: protocol(). @@ -2012,7 +2018,11 @@ enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; -enc_protocol(Type, Proto) -> throw({error, {invalid_protocol, {Type, Proto}}}). +enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; +enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; +enc_protocol(raw, {raw, P} = RAW) when is_integer(P) -> RAW; +enc_protocol(Type, Proto) -> + throw({error, {invalid_protocol, {Type, Proto}}}). -spec enc_send_flags(Flags) -> non_neg_integer() when @@ -2297,6 +2307,9 @@ enc_setopt_value(ipv6, Opt, V, _D, _T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso is_boolean(V) -> V; +enc_setopt_value(ipv6, router_alert, V, _D, T, _P) + when is_integer(V) andalso (T =:= raw) -> + V; enc_setopt_value(ipv6, rthdr, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; @@ -2749,8 +2762,8 @@ enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P) ?SOCKET_OPT_IPV6_RECVPKTINFO; enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, router_alert = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, router_alert = _Opt, _Dir, _D, T, _P) when (T =:= raw) -> + ?SOCKET_OPT_IPV6_ROUTER_ALERT; enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> ?SOCKET_OPT_IPV6_RTHDR; -- cgit v1.2.3