erl -noshell -pa . -eval 'tos_demo:run(), halt(0).'
-module(tos_demo).
-export([run/0]).
run() ->
{ok, R} = gen_udp:open(0, [{inet_backend, socket}, inet, binary,
{active, false}, {recvtos, true}]),
{ok, Rport} = inet:port(R),
{ok, S} = gen_udp:open(0, [{inet_backend, socket}, inet, binary,
{active, false}]),
try
lists:foreach(
fun(V) ->
ok = inet:setopts(S, [{tos, V}]),
ok = gen_udp:send(S, {127, 0, 0, 1}, Rport, <<".">>),
{ok, {_, _, Anc, _}} = gen_udp:recv(R, 100, 2000),
io:format("Val=~w 0x~.16B -> Anc ~p~n", [V, V, Anc])
end,
[8, 9, 136, 137]),
lists:foreach(
fun({Lab, Dscp}) ->
B = Dscp bsl 2,
ok = inet:setopts(S, [{tos, B}]),
ok = gen_udp:send(S, {127, 0, 0, 1}, Rport, <<"x">>),
{ok, {_, _, Anc, _}} = gen_udp:recv(R, 500, 2000),
io:format("~s -> Anc ~p~n", [Lab, Anc])
end,
[{"DSCP_2", 2}, {"DSCP_34_AF41", 34}])
after
gen_udp:close(R), gen_udp:close(S)
end.
Describe the bug
databinary and keepsvalueonly.inetsays ancillary TOS is {'tos', byte()} and options use {tos, non_neg_integer()}. Atoms likethroughputare notbyte()/non_neg_integer()— the documented contract is broken on receive.To Reproduce
Save as tos_demo.erl, then: erlc tos_demo.erl and
erl -noshell -pa . -eval 'tos_demo:run(), halt(0).'
Expected behavior
inet/gen_udpancillary receive should be{'tos', non_neg_integer()}in0..255(the wire octet).ip_tos()atoms must not appear there; those belong to the socket API’s symbolic layer, not a substitute for byte() in inet.datain the gen_udp path (today it is discarded in ctrl2ancdata/2).Affected versions
Any OTP shipping the
socketNIF (practical use OTP 22+; related work from ~2018 in history). Relevant wheninet_backend = socketfor gen_udp.Code
encode_ip_tos(https://github.com/erlang/otp/blob/master/erts/emulator/nifs/common/prim_socket_nif.c#L16245-L16279), mask (https://github.com/erlang/otp/blob/master/erts/emulator/nifs/common/prim_socket_nif.c#L436-L441)esock_cmsg_encode_ip_tos(https://github.com/erlang/otp/blob/master/erts/emulator/nifs/common/prim_socket_nif.c#L14204-L14217),esock_getopt_tos(https://github.com/erlang/otp/blob/master/erts/emulator/nifs/common/prim_socket_nif.c#L12580-L12599)ctrl2ancdata/2(https://github.com/erlang/otp/blob/master/lib/kernel/src/gen_udp_socket.erl#L2228-L2252)Additional context
Raw octet is still available via
socket:recvmsg(data in ctrl); not viagen_udpAncData as above.IPv6 tclass CMSG uses integer encoding in the same NIF; IPv4 TOS is special-cased with legacy atom mapping.