@@ -34,6 +34,9 @@ References:
3434
3535- Multicast Listener Discovery Version 2 (MLDv2) for IPv6
3636 http://tools.ietf.org/html/rfc3810
37+
38+ - IPv6 Router Advertisement Options for DNS Configuration
39+ https://tools.ietf.org/html/rfc8106
3740*)
3841
3942let src = Logs.Src. create " ndpc6" ~doc: " Mirage IPv6 discovery"
@@ -255,12 +258,17 @@ type pfx =
255258 pfx_preferred_lifetime : time option ;
256259 pfx_prefix : Ipaddr.Prefix .t }
257260
261+ type rdnss =
262+ { rdnss_lifetime : time option ;
263+ rdnss_addresses : Ipaddr .t list }
264+
258265type ra =
259266 { ra_cur_hop_limit : int ;
260267 ra_router_lifetime : time ;
261268 ra_reachable_time : time option ;
262269 ra_retrans_timer : time option ;
263270 ra_slla : Macaddr .t option ;
271+ ra_rdnss : rdnss list ;
264272 ra_prefix : pfx list }
265273
266274type na =
@@ -665,6 +673,60 @@ module NeighborCache = struct
665673 | Not_found -> false
666674end
667675
676+ module RDNSSList = struct
677+
678+ type t =
679+ (Ipaddr .t * time ) list
680+
681+ let empty =
682+ []
683+
684+ let to_list rdnssl =
685+ List. map fst rdnssl
686+
687+ let add rdnssl ~now ?(lifetime = Duration. of_year 1 ) ip =
688+ (ip, Int64. add now lifetime) :: rdnssl
689+
690+ let tick rdnssl ~now =
691+ List. filter (fun (_ , t ) -> t > now) rdnssl
692+
693+ let handle_ra rdnssl ~now ~src ~lft =
694+ match List. mem_assoc src rdnssl with
695+ | true ->
696+ let rdnssl = List. remove_assoc src rdnssl in
697+ if lft > 0L then begin
698+ Log. info (fun f -> f " RA: Refreshing Nameserver: src=%a lft=%Lu" Ipaddr. pp src lft);
699+ (src, Int64. add now lft) :: rdnssl, []
700+ end else begin
701+ Log. info (fun f -> f " RA: Nameserver Expired: src=%a" Ipaddr. pp src);
702+ rdnssl, []
703+ end
704+ | false ->
705+ if lft > 0L then begin
706+ Log. debug (fun f -> f " RA: Adding Nameserver: src=%a" Ipaddr. pp src);
707+ (add rdnssl ~now ~lifetime: lft src), []
708+ end else
709+ rdnssl, []
710+
711+ let add rdnssl ~now :_ ip =
712+ match List. mem_assoc ip rdnssl with
713+ | true -> rdnssl
714+ | false -> (ip, Duration. of_year 1 ) :: rdnssl
715+
716+ let select rdnssl reachable ip =
717+ let rec loop = function
718+ | [] ->
719+ begin match rdnssl with
720+ | [] -> ip, rdnssl
721+ | (ip , _ ) as r :: rest ->
722+ ip, rest @ [r]
723+ end
724+ | (ip , _ ) :: _ when reachable ip -> ip, rdnssl
725+ | _ :: rest -> loop rest
726+ in
727+ loop rdnssl
728+ end
729+
668730module RouterList = struct
669731
670732 type t =
@@ -741,6 +803,7 @@ module Parser = struct
741803 | TLLA of Macaddr .t
742804 | MTU of int
743805 | PREFIX of pfx
806+ | RDNSS of rdnss
744807
745808 let rec parse_options1 opts =
746809 if Cstruct. length opts > = Ipv6_wire. sizeof_opt then
@@ -777,6 +840,25 @@ module Parser = struct
777840 {pfx_on_link; pfx_autonomous; pfx_valid_lifetime; pfx_preferred_lifetime; pfx_prefix}
778841 in
779842 PREFIX pfx :: parse_options1 opts
843+ | 25 , 3 ->
844+ let rdnss_lifetime =
845+ let n = Ipv6_wire. get_opt_rdnss_header_rdnss_lifetime opt in
846+ match n with
847+ | 0l -> None
848+ | n -> Some (Int64. of_int32 n)
849+ in
850+ let decode_ns off = ipaddr_of_cstruct (Cstruct. shift opt off) in
851+ let rec collect_ns acc = function
852+ | 0 -> acc
853+ | n ->
854+ let ns = decode_ns (Ipv6_wire. sizeof_opt_rdnss_header + n * 16 ) in
855+ collect_ns (ns :: acc) (n - 1 )
856+ in
857+ let rdnss_addresses = collect_ns [] (Ipv6_wire. get_opt_rdnss_header_len opt - 1 ) in
858+ let rdnss =
859+ {rdnss_lifetime; rdnss_addresses}
860+ in
861+ RDNSS rdnss :: parse_options1 opts
780862 | ty , len ->
781863 Log. info (fun f -> f " ND6: Unsupported ND option in RA: ty=%d len=%d" ty len);
782864 parse_options1 opts
@@ -1133,6 +1215,7 @@ let local ~handle_ra ~now ~random mac =
11331215 let ctx =
11341216 { neighbor_cache = NeighborCache. empty;
11351217 prefix_list = PrefixList. link_local;
1218+ rdnss_list = RDNSSList. empty;
11361219 router_list = RouterList. empty;
11371220 mac = mac;
11381221 address_list = AddressList. empty;
@@ -1315,6 +1398,7 @@ let tick ~now ctx =
13151398 let address_list, actions = AddressList. tick ctx.address_list ~now ~retrans_timer in
13161399 let prefix_list = PrefixList. tick ctx.prefix_list ~now in
13171400 let neighbor_cache, actions' = NeighborCache. tick ctx.neighbor_cache ~now ~retrans_timer in
1401+ let rdnss_list = RDNSSList. tick ctx.rdnss_list ~now in
13181402 let router_list = RouterList. tick ctx.router_list ~now in
13191403 let ctx = {ctx with address_list; prefix_list; neighbor_cache; router_list} in
13201404 let actions = actions @ actions' in
@@ -1327,6 +1411,13 @@ let add_prefix ~now ctx pfx =
13271411let get_prefix ctx =
13281412 PrefixList. to_list ctx.prefix_list
13291413
1414+ let add_rdnss ~now ctx ips =
1415+ let rdnss_list = List. fold_left (RDNSSList. add ~now ) ctx.rdnss_list ips in
1416+ {ctx with rdnss_list}
1417+
1418+ let get_rdnss ctx =
1419+ RDNSSList. to_list ctx.rdnss_list
1420+
13301421let add_routers ~now ctx ips =
13311422 let router_list = List. fold_left (RouterList. add ~now ) ctx.router_list ips in
13321423 {ctx with router_list}
0 commit comments