@@ -34,6 +34,9 @@ References:
34
34
35
35
- Multicast Listener Discovery Version 2 (MLDv2) for IPv6
36
36
http://tools.ietf.org/html/rfc3810
37
+
38
+ - IPv6 Router Advertisement Options for DNS Configuration
39
+ https://tools.ietf.org/html/rfc8106
37
40
*)
38
41
39
42
let src = Logs.Src. create " ndpc6" ~doc: " Mirage IPv6 discovery"
@@ -255,12 +258,17 @@ type pfx =
255
258
pfx_preferred_lifetime : time option ;
256
259
pfx_prefix : Ipaddr.Prefix .t }
257
260
261
+ type rdnss =
262
+ { rdnss_lifetime : time option ;
263
+ rdnss_addresses : Ipaddr.V6 .t list }
264
+
258
265
type ra =
259
266
{ ra_cur_hop_limit : int ;
260
267
ra_router_lifetime : time ;
261
268
ra_reachable_time : time option ;
262
269
ra_retrans_timer : time option ;
263
270
ra_slla : Macaddr .t option ;
271
+ ra_rdnss : rdnss list ;
264
272
ra_prefix : pfx list }
265
273
266
274
type na =
@@ -665,6 +673,60 @@ module NeighborCache = struct
665
673
| Not_found -> false
666
674
end
667
675
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
+
668
730
module RouterList = struct
669
731
670
732
type t =
@@ -741,6 +803,7 @@ module Parser = struct
741
803
| TLLA of Macaddr .t
742
804
| MTU of int
743
805
| PREFIX of pfx
806
+ | RDNSS of rdnss
744
807
745
808
let rec parse_options1 opts =
746
809
if Cstruct. length opts > = Ipv6_wire. sizeof_opt then
@@ -777,6 +840,25 @@ module Parser = struct
777
840
{pfx_on_link; pfx_autonomous; pfx_valid_lifetime; pfx_preferred_lifetime; pfx_prefix}
778
841
in
779
842
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_cstruct.V6. of_cstruct_exn (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
780
862
| ty , len ->
781
863
Log. info (fun f -> f " ND6: Unsupported ND option in RA: ty=%d len=%d" ty len);
782
864
parse_options1 opts
@@ -1133,6 +1215,7 @@ let local ~handle_ra ~now ~random mac =
1133
1215
let ctx =
1134
1216
{ neighbor_cache = NeighborCache. empty;
1135
1217
prefix_list = PrefixList. link_local;
1218
+ rdnss_list = RDNSSList. empty;
1136
1219
router_list = RouterList. empty;
1137
1220
mac = mac;
1138
1221
address_list = AddressList. empty;
@@ -1315,6 +1398,7 @@ let tick ~now ctx =
1315
1398
let address_list, actions = AddressList. tick ctx.address_list ~now ~retrans_timer in
1316
1399
let prefix_list = PrefixList. tick ctx.prefix_list ~now in
1317
1400
let neighbor_cache, actions' = NeighborCache. tick ctx.neighbor_cache ~now ~retrans_timer in
1401
+ let rdnss_list = RDNSSList. tick ctx.rdnss_list ~now in
1318
1402
let router_list = RouterList. tick ctx.router_list ~now in
1319
1403
let ctx = {ctx with address_list; prefix_list; neighbor_cache; router_list} in
1320
1404
let actions = actions @ actions' in
@@ -1327,6 +1411,13 @@ let add_prefix ~now ctx pfx =
1327
1411
let get_prefix ctx =
1328
1412
PrefixList. to_list ctx.prefix_list
1329
1413
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
+
1330
1421
let add_routers ~now ctx ips =
1331
1422
let router_list = List. fold_left (RouterList. add ~now ) ctx.router_list ips in
1332
1423
{ctx with router_list}
0 commit comments