File 3477-snmp-agent-test-Add-transport-kind-test-case-s.patch of Package erlang

From 9d24525a4bf347201a993df89e5f505cbe83356c Mon Sep 17 00:00:00 2001
From: Micael Karlberg <bmk@erlang.org>
Date: Fri, 11 Sep 2020 16:50:54 +0200
Subject: [PATCH 07/21] [snmp|agent|test] Add transport kind test case(s)

Add transport kind test case(s).
*Reqests* sent from the manager to the agent (which sends a reply)
and *traps* sent from the agent to the manager. Both handled on the
respective transports.

Also adjusted the info test case (net-if info has been change).

OTP-16649
---
 lib/snmp/test/modules.mk              |   1 +
 lib/snmp/test/snmp_agent_SUITE.erl    | 592 +++++++++++++++++++++++++-
 lib/snmp/test/snmp_agent_test_lib.erl |  68 ++-
 lib/snmp/test/snmp_otp16649_user.erl  |  93 ++++
 lib/snmp/test/snmp_test_mgr.erl       |   3 +
 lib/snmp/test/snmp_test_mgr_misc.erl  |  84 +++-
 6 files changed, 789 insertions(+), 52 deletions(-)
 create mode 100644 lib/snmp/test/snmp_otp16649_user.erl

diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index ba0aea21dd..66cfa07f3b 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -38,6 +38,7 @@ TEST_UTIL_MODULES = \
 	snmp_test_lib \
 	snmp_agent_test_lib \
 	snmp_agent_test_get \
+	snmp_otp16649_user \
 	snmp_manager_user \
 	snmp_manager_user_old \
 	snmp_manager_user_test_lib \
diff --git a/lib/snmp/test/snmp_agent_SUITE.erl b/lib/snmp/test/snmp_agent_SUITE.erl
index c676bc487e..36e52ce207 100644
--- a/lib/snmp/test/snmp_agent_SUITE.erl
+++ b/lib/snmp/test/snmp_agent_SUITE.erl
@@ -303,7 +303,14 @@
 
 	 %% tickets2
 	 otp8395/1, 
-	 otp9884/1
+	 otp9884/1,
+         otp16649_1/1,
+         otp16649_2/1,
+         otp16649_3/1,
+         otp16649_4/1,
+         otp16649_5/1,
+         otp16649_6/1,
+         otp16649_7/1
 	]).
 
 %% Internal exports
@@ -420,6 +427,9 @@
 	 mnesia_start/0, 
 	 mnesia_stop/0, 
 	 start_standalone_agent/1, 
+	 stop_standalone_agent/1, 
+	 start_standalone_manager/1, 
+	 stop_standalone_manager/1, 
 	 do_info/1
 	]).
 
@@ -441,6 +451,9 @@
 -define(sa, [1,3,6,1,4,1,193,2]).
 -define(system, [1,3,6,1,2,1,1]).
 -define(snmp, [1,3,6,1,2,1,11]).
+-define(sysDescr_instance, [1,3,6,1,2,1,1,1,0]).
+-define(sysObjectID_instance, [1,3,6,1,2,1,1,2,0]).
+-define(sysUpTime_instance, [1,3,6,1,2,1,1,3,0]).
 -define(snmpTraps, [1,3,6,1,6,3,1,1,5]).
 -define(ericsson, [1,3,6,1,4,1,193]).
 -define(testTrap, [1,3,6,1,2,1,15,0]).
@@ -456,6 +469,11 @@
 
 -define(TRAP_UDP, 5000).
 
+-define(MGR_PORT,       5000).
+-define(MGR_MMS,        1024).
+-define(MGR_ENGINE_ID,  "mgrEngine").
+
+
 -define(tooBigStr, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").
 
 -define(str(X), snmp_pdus:bits_to_str(X)).
@@ -552,7 +570,8 @@ groups() ->
      {tickets2,                      [], tickets2_cases()}, 
      {otp4394,                       [], [otp_4394]},
      {otp7157,                       [], [otp_7157]},
-     {otp16092,                      [], otp16092_cases()}
+     {otp16092,                      [], otp16092_cases()},
+     {otp16649,                      [], otp16649_cases()}
     ].
 
 
@@ -858,6 +877,41 @@ init_per_testcase1(otp9884 = Case, Config) when is_list(Config) ->
 	 "~n   Case:   ~p"
 	 "~n   Config: ~p", [Case, Config]),
     otp9884({init, init_per_testcase2(Case, Config)});
+init_per_testcase1(otp16649_1 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_1_init(init_per_testcase2(Case, Config));
+init_per_testcase1(otp16649_2 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_2_init(init_per_testcase2(Case, Config));
+init_per_testcase1(otp16649_3 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_3_init(init_per_testcase2(Case, Config));
+init_per_testcase1(otp16649_4 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_4_init(init_per_testcase2(Case, Config));
+init_per_testcase1(otp16649_5 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_5_init(init_per_testcase2(Case, Config));
+init_per_testcase1(otp16649_6 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_6_init(init_per_testcase2(Case, Config));
+init_per_testcase1(otp16649_7 = Case, Config) when is_list(Config) ->
+    ?DBG("init_per_testcase1 -> entry with"
+	 "~n   Case:   ~p"
+	 "~n   Config: ~p", [Case, Config]),
+    otp16649_7_init(init_per_testcase2(Case, Config));
 init_per_testcase1(otp_7157 = _Case, Config) when is_list(Config) ->
     ?DBG("init_per_testcase1 -> entry with"
 	 "~n   Case:   ~p"
@@ -976,6 +1030,20 @@ end_per_testcase1(otp8395, Config) when is_list(Config) ->
     otp8395({fin, Config});
 end_per_testcase1(otp9884, Config) when is_list(Config) ->
     otp9884({fin, Config});
+end_per_testcase1(otp16649_1, Config) when is_list(Config) ->
+    otp16649_1_fin(Config);
+end_per_testcase1(otp16649_2, Config) when is_list(Config) ->
+    otp16649_2_fin(Config);
+end_per_testcase1(otp16649_3, Config) when is_list(Config) ->
+    otp16649_3_fin(Config);
+end_per_testcase1(otp16649_4, Config) when is_list(Config) ->
+    otp16649_4_fin(Config);
+end_per_testcase1(otp16649_5, Config) when is_list(Config) ->
+    otp16649_5_fin(Config);
+end_per_testcase1(otp16649_6, Config) when is_list(Config) ->
+    otp16649_6_fin(Config);
+end_per_testcase1(otp16649_7, Config) when is_list(Config) ->
+    otp16649_7_fin(Config);
 end_per_testcase1(_Case, Config) when is_list(Config) ->
     ?DBG("end_per_testcase1 -> entry with"
 	 "~n   Case:   ~p"
@@ -4399,7 +4467,7 @@ sa_mib() ->
     ok.
 
 ma_trap1(MA) ->
-    ok = snmpa:send_trap(MA, testTrap2, "standard trap"), 
+    ok = snmpa:send_trap(MA, testTrap2, "standard trap"),
     ?line ?expect5(trap, [system], 6, 1, [{[system, [4,0]],
 					   "{mbj,eklas}@erlang.ericsson.se"}]),
     ok = snmpa:send_trap(MA, testTrap1, "standard trap"),
@@ -7032,12 +7100,12 @@ otp16092_try_start_and_stop_agent(Node, Opts, Expected) ->
     ?IPRINT("try start snmp (agent) supervisor (on ~p) - expect ~p", 
             [Node, Expected]),
     case start_standalone_agent(Node, Opts) of
-        Pid when is_pid(Pid) andalso (Expected =:= success) ->
+        {ok, Pid} when is_pid(Pid) andalso (Expected =:= success) ->
             ?IPRINT("Expected success starting snmp (agent) supervisor"),
             ?SLEEP(1000),
             stop_standalone_agent(Pid),
             ok;
-        Pid when is_pid(Pid) andalso (Expected =:= failure) ->
+        {ok, Pid} when is_pid(Pid) andalso (Expected =:= failure) ->
             ?EPRINT("Unexpected success starting snmp (agent) supervisor: (~p)",
                     [Pid]),
             ?SLEEP(1000),
@@ -7094,7 +7162,6 @@ otp16092_try_start_and_stop_agent(Node, Opts, Expected) ->
     end,
     ok.
 
-            
 
 
 %%-----------------------------------------------------------------
@@ -7105,17 +7172,29 @@ otp16092_try_start_and_stop_agent(Node, Opts, Expected) ->
 tickets2_cases() ->
     [
      otp8395, 
-     otp9884
+     otp9884,
+     {group, otp16649}
     ].
 
+otp16649_cases() ->
+    [
+     otp16649_1,
+     otp16649_2,
+     otp16649_3,
+     otp16649_4,
+     otp16649_5,
+     otp16649_6,
+     otp16649_7
+    ].
+   
 
 otp8395({init, Config}) when is_list(Config) ->
     ?DBG("otp8395(init) -> entry with"
 	 "~n   Config: ~p", [Config]),
 
-    %% -- 
+    %% --
     %% Start nodes
-    %% 
+    %%
 
     {ok, AgentNode}    = start_node(agent),
     {ok, ManagerNode}  = start_node(manager),
@@ -7369,6 +7448,468 @@ otp9884_await_backup_completion(First, Second) ->
     throw({error, {bad_completion, First, Second}}).
 
 
+%%-----------------------------------------------------------------
+
+otp16649_1_init(Config) ->
+    AgentPreTransports = [{4000, req_responder},
+                          {4001, trap_sender}],
+    otp16649_init(1, AgentPreTransports, Config).
+
+otp16649_1_fin(Config) ->
+    otp16649_fin(1, Config).
+
+otp16649_1(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_1(Config) when is_list(Config) ->
+    otp16649(1, Config).
+
+
+otp16649_2_init(Config) ->
+    AgentPreTransports = [{4000,   req_responder},
+                          {system, trap_sender}],
+    otp16649_init(2, AgentPreTransports, Config).
+
+otp16649_2_fin(Config) ->
+    otp16649_fin(2, Config).
+
+otp16649_2(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_2(Config) when is_list(Config) ->
+    otp16649(2, Config).
+
+
+otp16649_3_init(Config) ->
+    AgentPreTransports = [{4000, req_responder},
+                          {0,    trap_sender}],
+    otp16649_init(3, AgentPreTransports, Config).
+
+otp16649_3_fin(Config) ->
+    otp16649_fin(3, Config).
+
+otp16649_3(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_3(Config) when is_list(Config) ->
+    otp16649(3, Config).
+
+
+otp16649_4_init(Config) ->
+    AgentPreTransports = [{4000,        req_responder},
+                          {{4000,4010}, trap_sender, [{no_reuse, true}]}],
+    otp16649_init(4, AgentPreTransports, Config).
+
+otp16649_4_fin(Config) ->
+    otp16649_fin(4, Config).
+
+otp16649_4(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_4(Config) when is_list(Config) ->
+    otp16649(4, Config).
+
+
+otp16649_5_init(Config) ->
+    AgentPreTransports = [{4000,        req_responder},
+                          {[{4000,4010}], trap_sender, [{no_reuse, true}]}],
+    otp16649_init(5, AgentPreTransports, Config).
+
+otp16649_5_fin(Config) ->
+    otp16649_fin(5, Config).
+
+otp16649_5(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_5(Config) when is_list(Config) ->
+    otp16649(5, Config).
+
+
+otp16649_6_init(Config) ->
+    AgentPreTransports = [{4000,        req_responder},
+                          {[4000,4001,4002], trap_sender, [{no_reuse, true}]}],
+    otp16649_init(6, AgentPreTransports, Config).
+
+otp16649_6_fin(Config) ->
+    otp16649_fin(5, Config).
+
+otp16649_6(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_6(Config) when is_list(Config) ->
+    otp16649(6, Config).
+
+
+otp16649_7_init(Config) ->
+    AgentPreTransports = [{4000,        req_responder},
+                          {[4000,{5100,5110}], trap_sender,
+                           [{no_reuse, true}]}],
+    otp16649_init(7, AgentPreTransports, Config).
+
+otp16649_7_fin(Config) ->
+    otp16649_fin(7, Config).
+
+otp16649_7(doc) ->
+    "OTP-16649 - Multiple transports.";
+otp16649_7(Config) when is_list(Config) ->
+    otp16649(7, Config).
+
+
+otp16649(N, Config) ->
+    ?IPRINT("otp16649 -> entry with"
+            "~n   N:      ~w"
+            "~n   Config: ~p", [N, Config]),
+
+    AgentNode   = ?config(agent_node, Config),
+    ManagerNode = ?config(manager_node, Config),
+
+    ?line AInfo = rpc:call(AgentNode, snmpa, info, []),
+
+    ?IPRINT("Agent Info: "
+            "~n      ~p", [AInfo]),
+
+    {value, {_, AgentRawTransports}} =
+        lists:keysearch(agent_raw_transports, 1, Config),
+    {value, {_, NetIF}}                =
+        lists:keysearch(net_if, 1, AInfo),
+    {value, {_, TIs}}                =
+        lists:keysearch(transport_info, 1, NetIF),
+    
+    if (length(AgentRawTransports) =:= length(TIs)) ->
+            ok;
+       true ->
+            ?IPRINT("Invalid transports: "
+                    "~n   Number of raw transports: ~w"
+                    "~n   Number of transports:     ~w",
+                    [length(AgentRawTransports), length(TIs)]),
+            exit({invalid_num_transports,
+                  length(AgentRawTransports), length(TIs)})
+    end,
+
+    ?IPRINT("validate transports"),
+    otp16649_validate_transports(AgentRawTransports, TIs),
+
+    ?IPRINT("which req-responder port-no"),
+    AgentReqPortNo = otp16649_which_req_port_no(TIs),
+
+    ?IPRINT("which trap-sender port-no"),
+    AgentTrapPortNo = otp16649_which_trap_port_no(TIs),
+
+    ?IPRINT("(mgr) register user"),
+    ?line ok = otp16649_mgr_reg_user(ManagerNode),
+
+    ?IPRINT("(mgr) register agent"),
+    TargetBase = "otp16649-agent-",
+    ReqTarget  = TargetBase ++ "req",
+    TrapTarget = TargetBase ++ "trap",
+
+    ?line ok = otp16649_mgr_reg_agent(ManagerNode, ReqTarget, AgentReqPortNo),
+    ?line ok = otp16649_mgr_reg_agent(ManagerNode, TrapTarget, AgentTrapPortNo),
+
+    ?IPRINT("(mgr) simple (sync) get request"),
+    Oids     = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance],
+    ?line ok = case otp16649_mgr_get_req(ManagerNode, Oids) of
+                   {ok, {noError, 0, ReplyOids}, _} ->
+                       ?IPRINT("(mgr) simple (sync) successful reply: "
+                               "~n      ~p", [ReplyOids]),
+                       ok;
+                   {ok, InvalidReply, _} ->
+                       ?IPRINT("(mgr) simple (sync) invalid reply: "
+                               "~n      ~p", [InvalidReply]),
+                       ok;
+                   {error, Reason} ->
+                       ?IPRINT("(mgr) simple (sync) error: "
+                               "~n      ~p", [Reason]),
+                       error
+               end,
+
+    ?IPRINT("load TestTrap..."), 
+    MibDir = ?config(mib_dir, Config),
+    ?line ok = otp16649_agent_load_mib(AgentNode, MibDir, "TestTrap"),
+
+    ?IPRINT("(agent) send trap"),
+    ?line ok = otp16649_agent_send_trap(AgentNode, testTrap2),
+
+    receive
+        {handle_trap, From, TrapTarget,
+         {?system, 6, 1, _, Vbs}} when is_pid(From) andalso
+                                        is_list(Vbs) ->
+            ?IPRINT("received expected handle trap callback message"),
+            ok
+
+    after 10000 ->
+            ?IPRINT("TIMEOUT"),
+            ?line exit(timeout)
+    end,
+
+    ?IPRINT("done"),
+    ok.
+
+
+otp16649_init(N, AgentPreTransports, Config) ->
+    ?IPRINT("otp16649_init -> entry with"
+            "~n   N:                  ~w"
+            "~n   AgentPreTransports: ~w"
+            "~n   Config:             ~p", [N, AgentPreTransports, Config]),
+
+    %% --
+    %% Start nodes
+    %%
+
+    ?IPRINT("start (agent and mansger) nodes"),
+
+    {ok, AgentNode}    = start_node(otp16649_mk_name(N, agent)),
+    {ok, ManagerNode}  = start_node(otp16649_mk_name(N, manager)),
+
+    %% --
+    %% Misc
+    %%
+
+    AgentHost         = ?HOSTNAME(AgentNode),
+    ManagerHost       = ?HOSTNAME(ManagerNode),
+
+    ?IPRINT("otp16649_init -> "
+            "~n      AgentHost:   ~p"
+            "~n      ManagerHost: ~p",
+            [AgentHost, ManagerHost]),
+
+    IpFamily          = inet,
+    Host              = snmp_test_lib:hostname(), 
+    Ip                = ?LOCALHOST(),
+    {ok, AgentIP}     = snmp_misc:ip(AgentHost),
+    {ok, ManagerIP0}  = snmp_misc:ip(ManagerHost),
+    ManagerIP         = tuple_to_list(ManagerIP0),
+    ?IPRINT("otp16649_init -> "
+            "~n      Host:       ~p"
+            "~n      Ip:         ~p"
+            "~n      AgentIP:    ~p"
+            "~n      ManagerIP0: ~p"
+            "~n      ManagerIP:  ~p",
+            [Host, Ip, AgentIP, ManagerIP0, ManagerIP]),
+
+
+    %% --
+    %% Write agent config
+    %% 
+
+    AgentConfDir        = ?config(agent_conf_dir, Config),
+    Vsns                = [v1,v2],
+    TransportDomain     = transportDomainUdpIpv4,
+    F = fun({PortInfo, Kind}) ->
+                #{addr => {AgentIP, PortInfo}, kind => Kind};
+           ({PortInfo, Kind, Opts}) ->
+                #{addr => {AgentIP, PortInfo}, kind => Kind, opts => Opts}
+        end,
+    AgentPreTransports2 = [F(T) || T <- AgentPreTransports],
+
+    ?IPRINT("write agent config files"),
+    ?line ok = snmp_config:write_agent_snmp_files(
+                 AgentConfDir, Vsns,
+                 TransportDomain, {ManagerIP, ?MGR_PORT}, AgentPreTransports2,
+                 "test"),
+
+    ?IPRINT("start agent"),
+    Config2 = start_agent([{host,          Host}, 
+			   {ip,            Ip}, 
+			   {ipfamily,      IpFamily},
+			   {agent_node,    AgentNode}, 
+			   {agent_host,    AgentHost}, 
+			   {agent_ip,      AgentIP}, 
+			   {manager_node,  ManagerNode},
+			   {manager_host,  ManagerHost}, 
+			   {manager_ip,    ManagerIP}|Config]),
+
+
+
+    %% --
+    %% Write manager config
+    %%
+
+    ?IPRINT("create manager dirs"),
+    MgrTopDir  = ?config(manager_top_dir, Config),
+    MgrDbDir   = filename:join(MgrTopDir,  "db/"),
+    MgrConfDir = filename:join(MgrTopDir,  "conf/"),
+    ?line ok   = file:make_dir(MgrConfDir),
+    MgrDbDir   = filename:join(MgrTopDir,  "db/"),
+    ?line ok   = file:make_dir(MgrDbDir),
+    MgrLogDir  = filename:join(MgrTopDir,  "log/"),
+    ?line ok   = file:make_dir(MgrLogDir),
+
+    ?IPRINT("write manager config files"),
+    MgrTransports = [{TransportDomain, {ManagerIP0, ?MGR_PORT}}],
+    ?line ok = snmp_config:write_manager_snmp_files(
+                 MgrConfDir,
+                 MgrTransports,
+                 ?MGR_MMS, ?MGR_ENGINE_ID),
+    
+    Config3 = [{manager_db_dir,   MgrDbDir},
+               {manager_conf_dir, MgrConfDir},
+               {manager_log_dir,  MgrLogDir} | Config2],
+               
+    ?IPRINT("start manager"),
+    ?line ok = start_manager(Config3),
+
+    ?DBG("otp16649_init -> done when"
+         "~n   Config2: ~p", [Config3]),
+    [{agent_raw_transports, AgentPreTransports} | Config3].
+
+otp16649_mk_name(N, Post) when is_integer(N) andalso is_atom(Post) ->
+    list_to_atom(?F("otp16649_~w_~w", [N, Post])).
+
+
+otp16649_fin(N, Config) when is_integer(N) ->
+    ?IPRINT("otp16649_fin -> entry with"
+            "~n   N:      ~p"
+            "~n   Config: ~p", [N, Config]),
+
+    ManagerNode = ?config(manager_node, Config),
+    AgentNode   = ?config(agent_node, Config),
+
+    %% -
+    %% Stop agent (this is the nice way to do it, 
+    %% so logs and files can be closed in the proper way).
+    %%
+
+    ?line AgentTopSup = ?config(agent_sup, Config),
+    stop_standalone_agent(AgentTopSup),
+
+
+    %% -
+    %% Stop manager
+    %%
+
+    stop_standalone_manager(ManagerNode),
+
+
+    %%
+    %% Stop the manager node
+    %%
+
+    ?DBG("otp16649_fin -> stop manager node", []),
+    stop_node(ManagerNode),
+
+
+    %%
+    %% Stop the agent node
+    %%
+
+    ?DBG("otp16649_fin -> stop agent node", []),
+    stop_node(AgentNode),
+
+    ?DBG("otp16649_fin -> done", []),
+    Config1 = lists:keydelete(manager_node, 1, Config),
+    lists:keydelete(agent_node, 1, Config1).
+
+
+otp16649_validate_transports([], []) ->
+    ok;
+otp16649_validate_transports([AgentRawTransport|AgentRawTransports],
+                             [TI|TIs]) ->
+    otp16649_validate_transport(AgentRawTransport, TI),
+    otp16649_validate_transports(AgentRawTransports, TIs).
+
+otp16649_validate_transport({PortInfo, Kind}, {PortNo, Kind, _}) ->
+    ?IPRINT("validate ~w transport:"
+            "~n   PortNo:   ~w"
+            "~n   PortInfo: ~p",  [Kind, PortNo, PortInfo]),
+    otp16649_validate_port(PortInfo, PortNo);
+otp16649_validate_transport({_, ConfKind}, {PortNo, ActualKind, _}) ->
+    exit({invalid_transport_kind, {PortNo, ConfKind, ActualKind}});
+otp16649_validate_transport({PortInfo, Kind, _}, {PortNo, Kind, _}) ->
+    ?IPRINT("validate ~w transport:"
+            "~n   PortNo:   ~w"
+            "~n   PortInfo: ~p",  [Kind, PortNo, PortInfo]),
+    otp16649_validate_port(PortInfo, PortNo);
+otp16649_validate_transport({_, ConfKind, _}, {PortNo, ActualKind, _}) ->
+    exit({invalid_transport_kind, {PortNo, ConfKind, ActualKind}}).
+
+otp16649_validate_port(PortNo, PortNo) when is_integer(PortNo) ->
+    ok;
+otp16649_validate_port(0, PortNo) when is_integer(PortNo) ->
+    ok;
+otp16649_validate_port(system, PortNo) when is_integer(PortNo) ->
+    ok;
+otp16649_validate_port(Range, PortNo) when is_tuple(Range) ->
+    case otp16649_validate_port_range(Range, PortNo) of
+        ok ->
+            ok;
+        error ->
+            exit({invalid_transport_port_no, {Range, PortNo}})
+    end;
+otp16649_validate_port(Ranges, PortNo) when is_list(Ranges) ->
+    case otp16649_validate_port_ranges(Ranges, PortNo) of
+        ok ->
+            ok;
+        error ->
+            exit({invalid_transport_port_no, {Ranges, PortNo}})
+    end.
+
+
+otp16649_validate_port_range({Min, Max}, PortNo)
+  when is_integer(Min) andalso
+       is_integer(Max) andalso
+       is_integer(PortNo) andalso
+       (Min =< PortNo) andalso
+       (PortNo =< Max) ->
+    ok;
+otp16649_validate_port_range(_Range, _PortNo) ->
+    error.
+
+otp16649_validate_port_ranges([], _PortNo) ->
+    error;
+otp16649_validate_port_ranges([PortNo|_], PortNo) when is_integer(PortNo) ->
+    ok;
+otp16649_validate_port_ranges([Range|Ranges], PortNo) when is_tuple(Range) ->
+    case otp16649_validate_port_range(Range, PortNo) of
+        ok ->
+            ok;
+        error ->
+            otp16649_validate_port_ranges(Ranges, PortNo)
+    end;
+otp16649_validate_port_ranges([_|Ranges], PortNo) ->
+    otp16649_validate_port_ranges(Ranges, PortNo).
+
+
+otp16649_which_req_port_no(TIs) ->
+    ?IPRINT("otp16649_which_req_port_no -> entry with"
+            "~n      TIs: ~p", [TIs]),
+    otp16649_which_port_no(TIs, req_responder).
+
+otp16649_which_trap_port_no(TIs) ->
+    ?IPRINT("otp16649_which_trap_port_no -> entry with"
+            "~n      TIs: ~p", [TIs]),
+    otp16649_which_port_no(TIs, trap_sender).
+
+otp16649_which_port_no([], Kind) ->
+    exit({no_transport_port_no, Kind});
+otp16649_which_port_no([{PortNo, Kind, _}|_], Kind) ->
+    PortNo;
+otp16649_which_port_no([_|TIs], Kind) ->
+    otp16649_which_port_no(TIs, Kind).
+
+
+otp16649_mgr_reg_user(Node) ->
+    rpc:call(Node, snmpm, register_user,
+             [otp16649, snmp_otp16649_user, self()]).
+
+otp16649_mgr_reg_agent(Node, Target, PortNo) ->
+    Localhost  = snmp_test_lib:localhost(),
+    Config     = [{address,   Localhost},
+                  {port,      PortNo},
+                  {version,   v1},
+                  {tdomain,   transportDomainUdpIpv4},
+                  {engine_id, "agentEngine"}],
+    rpc:call(Node, snmpm, register_agent,
+              [otp16649, Target, Config]).
+
+otp16649_mgr_get_req(Node, Oids) ->
+    TargetName = "otp16649-agent-req",
+    rpc:call(Node, snmpm, sync_get2, [otp16649, TargetName, Oids]).
+
+otp16649_agent_load_mib(Node, MibDir, Mib) ->
+    rpc:call(Node, snmpa, unload_mib, [snmp_master_agent, Mib]), % For safety
+    MibPath = join(MibDir, Mib),
+    rpc:call(Node, snmpa, load_mib, [snmp_master_agent, MibPath]).
+
+otp16649_agent_send_trap(Node, testTrap2 = Trap) ->
+    rpc:call(Node, snmpa, send_trap,
+             [snmp_master_agent, Trap, "standard trap"]).
+
+
 %%-----------------------------------------------------------------
 
 agent_log_validation(Node) ->
@@ -7421,7 +7962,7 @@ start_agent(Config, Opts) ->
     
     process_flag(trap_exit, true),
 
-    AgentTopSup = start_standalone_agent(AgentNode, AgentConfig),
+    ?line {ok, AgentTopSup} = start_standalone_agent(AgentNode, AgentConfig),
 
     [{agent_sup, AgentTopSup} | Config].
     
@@ -7473,9 +8014,9 @@ start_standalone_agent(Config)  ->
     case snmpa_supervisor:start_link(normal, Config) of
         {ok, AgentTopSup} ->
             unlink(AgentTopSup),
-            AgentTopSup;
+            {ok, AgentTopSup};
         {error, {already_started, AgentTopSup}} ->
-            AgentTopSup;
+            {ok, AgentTopSup};
         {error, _} = ERROR ->
             ERROR
     end.
@@ -7499,6 +8040,31 @@ stop_standalone_agent(Pid) ->
     nkill(Pid, kill).
 
 
+start_manager(Config) ->
+    Node    = ?config(manager_node, Config),
+    ConfDir = ?config(manager_conf_dir, Config),
+    DbDir   = ?config(manager_db_dir, Config),
+
+    Opts = [{server, [{verbosity, trace}]},
+	    {net_if, [{verbosity, trace}]},
+	    {note_store, [{verbosity, trace}]},
+	    {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}],
+    ?line ok = start_standalone_manager(Node, Opts).
+
+
+start_standalone_manager(Node, Config) ->
+    rpc:call(Node, ?MODULE, start_standalone_manager, [Config]).
+
+start_standalone_manager(Config)  ->
+    snmpm:start(Config).
+
+
+stop_standalone_manager(Node) when (Node =/= node()) ->
+    rpc:call(Node, snmpm, stop, []);
+stop_standalone_manager(_) ->
+    snmpm:stop().
+
+
 nkill(Pid, Reason) ->
     nkill(Pid, Reason, 10).
 
@@ -7536,7 +8102,7 @@ do_info(MaNode) ->
     Keys = [vsns, 
 	    stats_counters, 
 	    {agent, [process_memory, db_memory]}, 
-	    {net_if, [process_memory, port_info, reqs]}, 
+	    {net_if, [process_memory, transport_info, reqs]}, 
 	    {note_store, [process_memory, db_memory]}, 
 	    {symbolic_store, [process_memory, db_memory]}, 
 	    {local_db, [process_memory, db_memory]}, 
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 629ec49db4..ce75e34649 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -475,22 +475,28 @@ tc_run(Mod, Func, Args, Opts) ->
             "~n   StdM:        ~p"
             "~n", [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]),
     case snmp_test_mgr:start_link([%% {agent, snmp_test_lib:hostname()},
-			      {packet_server_debug, true},
-			      {debug,               false},
-			      {agent,               get(master_host)}, 
-			      {ipfamily,            get(ipfamily)},
-			      {agent_udp,           4000},
-			      {trap_udp,            5000},
-			      {recbuf,              65535},
-			      quiet,
-			      Vsn, 
-			      {community,           Community},
-			      {user,                User},
-			      {sec_level,           SecLevel},
-			      {engine_id,           EngineID},
-			      {context_engine_id,   CtxEngineID},
-			      {dir,                 Dir},
-			      {mibs,                mibs(StdM, M)}]) of
+                                   {packet_server_debug, true},
+                                   {debug,               false},
+                                   {agent,               get(master_host)}, 
+                                   {ipfamily,            get(ipfamily)},
+                                   {agent_udp,           4000},
+                                   %% <SEP-TRANSPORTS>
+                                   %% First port is used to request replies
+                                   %% Second port is used for traps sent
+                                   %% by the agent.
+                                   %% {agent_udp,           {4000, 4001}},
+                                   %% </SEP-TRANSPORTS>
+                                   {trap_udp,            5000},
+                                   {recbuf,              65535},
+                                   quiet,
+                                   Vsn, 
+                                   {community,           Community},
+                                   {user,                User},
+                                   {sec_level,           SecLevel},
+                                   {engine_id,           EngineID},
+                                   {context_engine_id,   CtxEngineID},
+                                   {dir,                 Dir},
+                                   {mibs,                mibs(StdM, M)}]) of
 	{ok, _Pid} ->
 	    try apply(Mod, Func, Args) of
                 Res ->
@@ -1584,14 +1590,28 @@ config(Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily) ->
     ?line {Domain, ManagerAddr} =
 	case IpFamily of
 	    inet6 ->
-		Ipv6Domain = transportDomainUdpIpv6,
-		AgentIpv6Addr = {AIp, 4000},
-		ManagerIpv6Addr = {MIp, ?TRAP_UDP},
+		TransportDomain6 = transportDomainUdpIpv6,
+		AgentAddr6       = {AIp, 4000},
+		ManagerAddr6     = {MIp, ?TRAP_UDP},
 		?line ok =
 		    snmp_config:write_agent_snmp_files(
 		      AgentConfDir, Vsns,
-		      Ipv6Domain, ManagerIpv6Addr, AgentIpv6Addr, "test"),
-		{Ipv6Domain, ManagerIpv6Addr};
+		      TransportDomain6, ManagerAddr6, AgentAddr6, "test"),
+		{TransportDomain6, ManagerAddr6};
+	    inet ->
+		TransportDomain4 = transportDomainUdpIpv4,
+                AIp2 = maybe_fix_addr(AIp),
+		ManagerAddr4     = {MIp, ?TRAP_UDP},
+                %% AgentPreTransport  =
+                %%     [#{addr => {AIp2, 4000}, kind => req_responder},
+                %%      #{addr => {AIp2, 4001}, kind => trap_sender}],
+                AgentPreTransport  = [#{addr => {AIp2, 4000}}],
+		?line ok =
+		    snmp_config:write_agent_snmp_files(
+		      AgentConfDir, Vsns,
+		      TransportDomain4, ManagerAddr4, AgentPreTransport,
+                      "test"),
+		{TransportDomain4, ManagerAddr4};
 	    _ ->
 		?line ok =
 		    snmp_config:write_agent_snmp_files(
@@ -1614,6 +1634,12 @@ config(Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily) ->
     ?line write_notify_conf(AgentConfDir),
     ok.
 
+maybe_fix_addr(Addr) when is_list(Addr) ->
+    list_to_tuple(Addr);
+maybe_fix_addr(Addr) when is_tuple(Addr) ->
+    Addr.
+
+    
 delete_files(Config) ->
     AgentDir = ?config(agent_dir, Config),
     delete_files(AgentDir, [db, conf]).
diff --git a/lib/snmp/test/snmp_otp16649_user.erl b/lib/snmp/test/snmp_otp16649_user.erl
new file mode 100644
index 0000000000..260099a999
--- /dev/null
+++ b/lib/snmp/test/snmp_otp16649_user.erl
@@ -0,0 +1,93 @@
+%% 
+%% %CopyrightBegin%
+%% 
+%% Copyright Ericsson AB 2020-2020. All Rights Reserved.
+%% 
+%% Licensed 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.
+%% 
+%% %CopyrightEnd%
+%% 
+
+%%----------------------------------------------------------------------
+%% Purpose: Utility functions for the (snmp manager) user test(s).
+%%----------------------------------------------------------------------
+
+-module(snmp_otp16649_user).
+
+-behaviour(snmpm_user).
+
+
+%%----------------------------------------------------------------------
+%% Include files
+%%----------------------------------------------------------------------
+-include_lib("common_test/include/ct.hrl").
+-include("snmp_test_lib.hrl").
+
+
+%%----------------------------------------------------------------------
+%% External exports
+%%----------------------------------------------------------------------
+-export([
+        ]).
+
+
+%%----------------------------------------------------------------------
+%% Internal exports
+%%----------------------------------------------------------------------
+
+-export([
+	 handle_error/3,
+         handle_agent/5,
+         handle_pdu/4,
+         handle_trap/3,
+         handle_inform/3,
+         handle_report/3
+	]).
+
+
+%%----------------------------------------------------------------------
+%% User callback functions:
+%%----------------------------------------------------------------------
+
+handle_error(ReqId, Reason, UserPid) ->
+    UserPid ! {handle_error, self(), ReqId, Reason},
+    ignore.
+ 
+ 
+handle_agent(Addr, Port, SnmpInfo, UserPid, UserData) ->
+    UserPid ! {handle_agent, self(), Addr, Port, SnmpInfo, UserData},
+    ignore.
+ 
+ 
+handle_pdu(TargetName, ReqId, SnmpResponse, UserPid) ->
+    UserPid ! {handle_pdu, self(), TargetName, ReqId, SnmpResponse},
+    ignore.
+ 
+handle_trap(TargetName, SnmpTrap, UserPid) ->
+    UserPid ! {handle_trap, self(), TargetName, SnmpTrap},
+    ignore.
+ 
+handle_inform(TargetName, SnmpInform, UserPid) ->
+    UserPid ! {handle_inform, self(), TargetName, SnmpInform},
+    receive
+	{handle_inform_no_response, TargetName} ->
+	    no_reply;
+	{handle_inform_response, TargetName} ->
+	    ignore
+    end.
+
+handle_report(TargetName, SnmpReport, UserPid) ->
+    UserPid ! {handle_report, self(), TargetName, SnmpReport},
+    ignore.
+
+
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index f8eb85734e..e4b5eb0083 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -312,6 +312,9 @@ is_options_ok([{ipfamily,IpFamily}|Opts])
     is_options_ok(Opts);
 is_options_ok([{agent_udp,Int}|Opts]) when is_integer(Int) ->
     is_options_ok(Opts);
+is_options_ok([{agent_udp, {IntR, IntT}}|Opts]) when is_integer(IntR) andalso
+                                                     is_integer(IntT) ->
+    is_options_ok(Opts);
 is_options_ok([{trap_udp,Int}|Opts]) when is_integer(Int) ->
     is_options_ok(Opts);
 is_options_ok([{community,List}|Opts]) when is_list(List) ->
diff --git a/lib/snmp/test/snmp_test_mgr_misc.erl b/lib/snmp/test/snmp_test_mgr_misc.erl
index b1b1b8040b..2bfd44002f 100644
--- a/lib/snmp/test/snmp_test_mgr_misc.erl
+++ b/lib/snmp/test/snmp_test_mgr_misc.erl
@@ -62,9 +62,29 @@ start_link_packet(
 start_link_packet(
   InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz,
   Dbg, IpFamily) when is_integer(UdpPort) ->
+    do_start_link_packet(InHandler,
+                         AgentIp, UdpPort, TrapUdp,
+                         VsnHdr, Version, Dir, BufSz,
+                         Dbg, IpFamily);
+start_link_packet(InHandler,
+                  AgentIp, {AReqPort, ATrapPort} = UdpPorts, TrapUdp,
+                  VsnHdr, Version, Dir, BufSz,
+                  Dbg, IpFamily) when is_integer(AReqPort) andalso
+                                      is_integer(ATrapPort) ->
+    do_start_link_packet(InHandler,
+                         AgentIp, UdpPorts, TrapUdp,
+                         VsnHdr, Version, Dir, BufSz,
+                         Dbg, IpFamily).
+
+do_start_link_packet(InHandler,
+                     AgentIp, UdpPorts, TrapUdp,
+                     VsnHdr, Version, Dir, BufSz,
+                     Dbg, IpFamily) ->
     Args =
 	[self(),
-	 InHandler, AgentIp, UdpPort, TrapUdp,  VsnHdr, Version, Dir, BufSz,
+	 InHandler,
+         AgentIp, UdpPorts, TrapUdp,
+         VsnHdr, Version, Dir, BufSz,
 	 Dbg, IpFamily],
     proc_lib:start_link(?MODULE, init_packet, Args).
 
@@ -100,7 +120,7 @@ send_bytes(Bytes, PacketPid) ->
 %%--------------------------------------------------
 init_packet(
   Parent,
-  SnmpMgr, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz,
+  SnmpMgr, AgentIp, UdpPorts, TrapUdp, VsnHdr, Version, Dir, BufSz,
   DbgOptions, IpFamily) ->
     %% This causes "verbosity printouts" to print (from the
     %% specified level) in the modules we "borrow" from the
@@ -117,7 +137,7 @@ init_packet(
     init_usm(Version, Dir),
     proc_lib:init_ack(Parent, self()),
     ?IPRINT("started"),
-    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []).
+    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version, []).
 
 init_debug(Dbg) when is_atom(Dbg) ->
     put(debug,Dbg),
@@ -139,7 +159,7 @@ init_debug(DbgOptions) when is_list(DbgOptions) ->
     ok.
 
 
-packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) ->
+packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version, MsgData) ->
     receive
 	{send_discovery_pdu, From, Pdu} ->
 	    d("packet_loop -> received send_discovery_pdu with"
@@ -151,9 +171,10 @@ packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) ->
 		{M, B} when is_list(B) -> 
 		    put(discovery, {M, From}),
 		    display_outgoing_message(M),
-		    udp_send(UdpId, AgentIp, UdpPort, B)
+                    Port = select_request_port(UdpPorts),
+		    udp_send(UdpId, AgentIp, Port, B)
 	    end,
-	    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []);
+	    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version, []);
 
 	{send_pdu, Pdu} ->
 	    d("packet_loop -> received send_pdu with"
@@ -161,10 +182,11 @@ packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) ->
 	    case mk_msg(Version, Pdu, VsnHdr, MsgData) of
 		error ->
 		    ok;
-		B when is_list(B) -> 
-		    udp_send(UdpId, AgentIp, UdpPort, B)
+		B when is_list(B) ->
+                    Port = select_request_port(UdpPorts),
+		    udp_send(UdpId, AgentIp, Port, B)
 	    end,
-	    packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
+	    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version, []);
 
 	{send_msg, Msg, Ip, Udp} ->
 	    d("packet_loop -> received send_msg with"
@@ -179,38 +201,64 @@ packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) ->
 		B when is_list(B) -> 
 		    udp_send(UdpId, Ip, Udp, B)
 	    end,
-	    packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
-	{udp, UdpId, Ip, UdpPort, Bytes} ->
+	    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version, []);
+
+	{udp, UdpId, Ip, Port, Bytes} ->
 	    d("packet_loop -> received udp with"
 	      "~n   UdpId:     ~p"
 	      "~n   Ip:        ~p"
-	      "~n   UdpPort:   ~p"
-	      "~n   sz(Bytes): ~p", [UdpId, Ip, UdpPort, sz(Bytes)]),	    
+	      "~n   Port:      ~p (~p)"
+	      "~n   sz(Bytes): ~p", [UdpId, Ip, Port, UdpPorts, sz(Bytes)]),
+            case UdpPorts of
+                Port ->
+                    ok;
+                {Port, _} -> % Should be a (request) response
+                    ok;
+                {_, Port} -> % Should be a trap
+                    ok;
+                _ ->
+                    d("packet_loop -> received packet from unknown port"
+                      "~n   ~p", [Port]),
+                    exit({snmp_packet_from_unknown_port, Port, UdpPorts})
+            end,
 	    MsgData3 = handle_udp_packet(Version, erase(discovery),
-					 UdpId, Ip, UdpPort, Bytes,
+					 UdpId, Ip, Port, Bytes,
 					 SnmpMgr, AgentIp),
-	    packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,
+	    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version,
 			MsgData3);
+
         {udp_error, UdpId, Reason} ->
 	    gen_udp:close(UdpId),
 	    exit({udp_error, Reason});
 
 	{send_bytes, B} ->
 	    d("packet_loop -> received send_bytes with"
-	      "~n   sz(B): ~p", [sz(B)]),	    
-	    udp_send(UdpId, AgentIp, UdpPort, B),
-	    packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
+	      "~n   sz(B): ~p", [sz(B)]),
+            Port = select_request_port(UdpPorts),
+	    udp_send(UdpId, AgentIp, Port, B),
+	    packet_loop(SnmpMgr, UdpId, AgentIp, UdpPorts, VsnHdr, Version, []);
+
 	{stop, Pid} ->
 	    d("packet_loop -> received stop from ~p", [Pid]),	    
 	    gen_udp:close(UdpId),
 	    Pid ! {self(), stopped},
 	    exit(normal);
+
 	Other ->
 	    d("packet_loop -> received unknown"
 	      "~n   ~p", [Other]),	    
 	    exit({snmp_packet_got_other, Other})
     end.
 
+select_request_port({Port, _}) when is_integer(Port) ->
+    Port;
+select_request_port(Port) when is_integer(Port) ->
+    Port.
+
+%% select_trap_port({_, Port}) when is_integer(Port) ->
+%%     Port;
+%% select_trap_port(Port) when is_integer(Port) ->
+%%     Port.
 
 handle_udp_packet(_V, undefined, 
 		  UdpId, Ip, UdpPort,
-- 
2.26.2

openSUSE Build Service is sponsored by