From a30d8882d7d98bbf285186699fe21c19cc5a6fab Mon Sep 17 00:00:00 2001 From: Sebastien Merle <s.merle@gmail.com> Date: Fri, 30 Sep 2022 09:42:44 -0700 Subject: [PATCH] Add support for PCE initiated LSPs --- .../epce/src/epce_pcep_server_handler.erl | 5 ++ src/te/apps/epce/src/epce_server.erl | 83 +++++++++++++++++-- src/te/rebar.lock | 4 +- tutorial/2-6-netgen-topology.yml.template | 15 +--- tutorial/2-6-te-demo-start-testbed.sh | 2 - tutorial/2-6-te-demo.md | 17 ++++ 6 files changed, 100 insertions(+), 26 deletions(-) diff --git a/src/te/apps/epce/src/epce_pcep_server_handler.erl b/src/te/apps/epce/src/epce_pcep_server_handler.erl index 210395885..df786bfc6 100644 --- a/src/te/apps/epce/src/epce_pcep_server_handler.erl +++ b/src/te/apps/epce/src/epce_pcep_server_handler.erl @@ -17,6 +17,7 @@ -export([init/1]). -export([opened/4]). -export([flow_added/2]). +-export([flow_initiated/2]). -export([ready/1]). -export([request_route/2]). -export([flow_delegated/2]). @@ -49,6 +50,10 @@ flow_added(Flow, State) -> ok -> {ok, State} end. +flow_initiated(Flow, State) -> + ok = epce_server:flow_initiated(Flow), + {ok, State}. + ready(State) -> {ok, State}. diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 11a1625ef..507255385 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -15,10 +15,12 @@ -export([start_link/0]). -export([get_flows/0]). -export([update_flow/2]). +-export([initiate_flow/4]). % Handler Functions -export([session_opened/3]). -export([flow_added/1]). +-export([flow_initiated/1]). -export([request_route/1]). -export([flow_status_changed/2]). @@ -32,6 +34,11 @@ -export([terminate/2]). +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(LARGE_TIMEOUT, 20000). + + %%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(sess, { @@ -60,6 +67,10 @@ get_flows() -> update_flow(FlowId, LabelStack) -> gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). +initiate_flow(Name, FromAddr, ToAddr, BindingLabel) -> + gen_server:call(?MODULE, {initiate_flow, Name, FromAddr, ToAddr, + BindingLabel}, ?LARGE_TIMEOUT). + %%% HANDLER FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -69,6 +80,9 @@ session_opened(Id, Caps, Pid) -> flow_added(Flow) -> gen_server:call(?MODULE, {flow_added, Flow}). +flow_initiated(Flow) -> + gen_server:call(?MODULE, {flow_initiated, Flow}). + request_route(RouteReq) -> gen_server:call(?MODULE, {request_route, RouteReq}). @@ -92,11 +106,26 @@ handle_call({update_flow, FlowId, Labels}, From, error -> {reply, {error, session_not_found}, State}; {ok, #sess{pid = Pid}} -> #{source := S, destination := D, constraints := C} = R, - ReqRoute = route_from_labels(S, D, C, Labels), + ReqRoute = routereq_from_labels(S, D, C, Labels), session_update_flow(State, Pid, FlowId, ReqRoute, From), {noreply, State} end end; +handle_call({initiate_flow, Name, FromAddr, ToAddr, Binding}, From, + #state{sessions = SessMap} = State) -> + case maps:find(FromAddr, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + case compute_path(FromAddr, ToAddr) of + {error, Reason} -> + {reply, {error, Reason}, State}; + {ok, Labels} -> + InitRoute = routeinit_from_labels(Name, FromAddr, ToAddr, + [], Binding, Labels), + session_initiate_flow(State, Pid, InitRoute, From), + {noreply, State} + end + end; handle_call({session_opened, Id, Caps, Pid}, _From, #state{sessions = SessMap, sess_pids = SessPids} = State) -> logger:debug("Session with capabilities ~w open to ~w", [Caps, Id]), @@ -114,6 +143,11 @@ handle_call({flow_added, #{id := Id, route := Route} = Flow}, _From, #state{flows = Flows} = State) -> logger:debug("Flow ~w with route ~w added", [Id, route_to_labels(Route)]), {reply, ok, State#state{flows = Flows#{Id => Flow}}}; +handle_call({flow_initiated, #{id := Id, route := Route} = Flow}, + _From, #state{flows = Flows} = State) -> + logger:debug("Flow ~w with route ~p initiated", + [Id, route_to_labels(Route)]), + {reply, ok, State#state{flows = Flows#{Id => Flow}}}; handle_call({request_route, RouteReq}, _From, State) -> logger:info("Route from ~w to ~w requested", [maps:get(source, RouteReq), maps:get(destination, RouteReq)]), @@ -122,7 +156,7 @@ handle_call({request_route, RouteReq}, _From, State) -> {error, _Reason} = Error -> {reply, Error, State}; {ok, Labels} -> - Route = route_from_labels(S, D, C, Labels), + Route = routereq_from_labels(S, D, C, Labels), {reply, {ok, Route}, State} end; handle_call({flow_status_changed, FlowId, NewStatus}, _From, @@ -131,12 +165,13 @@ handle_call({flow_status_changed, FlowId, NewStatus}, _From, Flow = maps:get(FlowId, Flows), {reply, ok, State#state{ flows = maps:put(FlowId, Flow#{status := NewStatus}, Flows)}}; -handle_call(Request, _From, State) -> - logger:warning("Unexpected request ~w", [Request]), +handle_call(Request, From, State) -> + logger:warning("Unexpected call from ~w: ~p", [From, Request]), {reply, {error, unexpected_call}, State}. -handle_cast(_Request, State) -> +handle_cast(Request, State) -> + logger:warning("Unexpected cast: ~p", [Request]), {noreply, State}. handle_continue(_Continue, State) -> @@ -153,7 +188,17 @@ handle_info({flow_updated, FlowId, NewRoute, From}, {noreply, State#state{flows = Flows2}} end; handle_info({flow_update_error, FlowId, Reason, From}, State) -> - logger:error("Flow ~w updated error: ~w", [FlowId, Reason]), + logger:error("Flow ~w updated error: ~p", [FlowId, Reason]), + gen_server:reply(From, {error, Reason}), + {noreply, State}; +handle_info({flow_initiated, #{id := FlowId, route := Route} = Flow, From}, + #state{flows = Flows} = State) -> + logger:info("Flow ~w initiated to ~p", + [FlowId, route_to_labels(Route)]), + gen_server:reply(From, {ok, FlowId}), + {noreply, State#state{flows = Flows#{FlowId => Flow}}}; +handle_info({flow_init_error, Reason, From}, State) -> + logger:error("Flow initialisation error: ~p", [Reason]), gen_server:reply(From, {error, Reason}), {noreply, State}; handle_info({'DOWN', MonRef, process, Pid, _Reason}, @@ -169,7 +214,8 @@ handle_info({'DOWN', MonRef, process, Pid, _Reason}, _X -> {noreply, State} end; -handle_info(_Info, State) -> +handle_info(Info, State) -> + logger:warning("Unexpected message: ~p", [Info]), {noreply, State}. code_change(_OldVsn, State, _Extra) -> @@ -189,10 +235,10 @@ compute_path(From, To) -> {ok, Labels}; {error, Reason} -> logger:warning("Failed to find a route from ~p to ~p", [From, To]), - {error, Reason} + {error, route_not_found} end. -route_from_labels(Source, Destination, Constraints, Labels) -> +routereq_from_labels(Source, Destination, Constraints, Labels) -> #{ source => Source, destination => Destination, @@ -207,6 +253,13 @@ route_from_labels(Source, Destination, Constraints, Labels) -> ] }. +routeinit_from_labels(Name, Source, Destination, Constraints, Binding, Labels) -> + Route = routereq_from_labels(Source, Destination, Constraints, Labels), + Route#{ + name => Name, + binding_label => Binding + }. + route_to_labels(#{steps := Steps}) -> [Sid#mpls_stack_entry.label || #{sid := Sid} <- Steps]. @@ -216,6 +269,9 @@ route_to_labels(#{steps := Steps}) -> session_update_flow(#state{bouncer = Pid}, SessPid, FlowId, Route, Args) -> Pid ! {update_flow, SessPid, FlowId, Route, Args}. +session_initiate_flow(#state{bouncer = Pid}, SessPid, Route, Args) -> + Pid ! {initiate_flow, SessPid, Route, Args}. + bouncer_start(#state{bouncer = undefined} = State) -> Self = self(), Pid = erlang:spawn_link(fun() -> @@ -238,5 +294,14 @@ bouncer_loop(Parent) -> {error, Reason} -> Parent ! {flow_update_error, FlowId, Reason, Args}, bouncer_loop(Parent) + end; + {initiate_flow, SessPid, InitRoute, Args} -> + case pcep_server_session:initiate_flow(SessPid, InitRoute) of + {ok, Flow} -> + Parent ! {flow_initiated, Flow, Args}, + bouncer_loop(Parent); + {error, Reason} -> + Parent ! {flow_init_error, Reason, Args}, + bouncer_loop(Parent) end end. diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 6eb067ecd..ef9747fe4 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -11,11 +11,11 @@ {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, {<<"pcep_codec">>, {git,"git@github.com:stritzinger/pcep_codec.git", - {ref,"3d1623fdf0c62d3daf400ac65aaf985f8bd40835"}}, + {ref,"84dcc430b2aa427984c100cc19dd35d946b22ff9"}}, 1}, {<<"pcep_server">>, {git,"git@github.com:stritzinger/pcep_server.git", - {ref,"2cf692e7e5fa2e9ac0fd54e5aa64ffb17f4f1b4a"}}, + {ref,"3910bf5546879cb91a3483008b6aed11753deaa6"}}, 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ diff --git a/tutorial/2-6-netgen-topology.yml.template b/tutorial/2-6-netgen-topology.yml.template index 286f40005..d16e34841 100644 --- a/tutorial/2-6-netgen-topology.yml.template +++ b/tutorial/2-6-netgen-topology.yml.template @@ -103,17 +103,11 @@ routers: debug pathd pcep basic segment-routing traffic-eng - mpls-te on - policy color 1 endpoint 6.6.6.6 - name DEFAULT - binding-sid 1111 - candidate-path preference 100 name RUNTIME dynamic - ! - ! pcep pce-config CONFIG source-address ip 1.1.1.1 pce PCE + pce-initiated address ip ${PCE_IP} config CONFIG pcc @@ -438,16 +432,11 @@ routers: debug pathd pcep segment-routing traffic-eng - policy color 1 endpoint 1.1.1.1 - name DEFAULT - binding-sid 6666 - candidate-path preference 200 name RUNTIME dynamic - ! - ! pcep pce-config CONFIG source-address ip 6.6.6.6 pce PCE + pce-initiated address ip ${PCE_IP} config CONFIG pcc diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh index 3266d1141..5ba48479c 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -41,7 +41,5 @@ cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topolo sudo -i bash -c "\ cd ${RUNDIR}/netgen;\ - sysctl -w net.ipv6.conf.all.disable_ipv6=1;\ - sysctl -w net.ipv6.conf.default.disable_ipv6=1;\ sysctl -w net.ipv4.conf.all.rp_filter=0;\ PATH=/usr/lib/frr:\$PATH ./exe/netgen ../topology.yml -c ../config.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 774a38bab..da822e3a3 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -89,3 +89,20 @@ Then in second console: $ sudo -i # cd /tmp/negen # ./tmux.sh + +### Setup a flow from the Erlang console + +We will setup two unidirectional flow between router 1 and 6. +We will use the binding label 1111 for the flow from router 1 to router 6, and the binding label 6666 for the flow from router 6 to router 1. + + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). + 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). + +Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: + + $ ping 9.9.9.2 + +From the Erlang console we can update the initiated flows to change the path the packets are flowing through: + + 3> epce_server:update_flow(Flow6to1, [16050, 16030, 16010]). -- GitLab