Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 55 additions & 81 deletions ocaml/networkd/bin/network_server.ml
Original file line number Diff line number Diff line change
Expand Up @@ -218,30 +218,10 @@ let reset_state () =
config := Network_config.read_management_conf reset_order

let set_gateway_interface _dbg name =
(* Remove dhclient conf (if any) for the old and new gateway interfaces.
* This ensures that dhclient gets restarted with an updated conf file when
* necessary. *)
( match !config.gateway_interface with
| Some old_iface when name <> old_iface ->
Dhclient.remove_conf_file name ;
Dhclient.remove_conf_file old_iface
| _ ->
()
) ;
debug "Setting gateway interface to %s" name ;
config := {!config with gateway_interface= Some name}

let set_dns_interface _dbg name =
(* Remove dhclient conf (if any) for the old and new DNS interfaces.
* This ensures that dhclient gets restarted with an updated conf file when
* necessary. *)
( match !config.dns_interface with
| Some old_iface when name <> old_iface ->
Dhclient.remove_conf_file name ;
Dhclient.remove_conf_file old_iface
| _ ->
()
) ;
debug "Setting DNS interface to %s" name ;
config := {!config with dns_interface= Some name}

Expand Down Expand Up @@ -474,6 +454,17 @@ module Interface = struct
)
()

let config_to_dhcp_options config =
let gateway =
Option.fold ~none:[]
~some:(fun n -> [`gateway n])
config.gateway_interface
in
let dns =
Option.fold ~none:[] ~some:(fun n -> [`dns n]) config.dns_interface
in
gateway @ dns

let get_ipv4_addr dbg name =
Debug.with_thread_associated dbg (fun () -> Ip.get_ipv4 name) ()

Expand All @@ -482,33 +473,27 @@ module Interface = struct
(fun () ->
debug "Configuring IPv4 address for %s: %s" name
(conf |> Rpcmarshal.marshal typ_of_ipv4 |> Jsonrpc.to_string) ;
update_config name {(get_config name) with ipv4_conf= conf} ;
let previous_config = get_config name in
let previous = previous_config.ipv4_conf in
update_config name {previous_config with ipv4_conf= conf} ;
(* deconfigure previous *)
Xapi_stdext_pervasives.Pervasiveext.ignore_exn (fun () ->
match previous with
| None4 ->
()
| DHCP4 ->
if conf <> DHCP4 then Dhclient.stop name
| Static4 _ -> (
match conf with Static4 _ -> () | _ -> Ip.flush_ip_addr name
)
) ;
(* configure conf *)
match conf with
| None4 ->
if List.mem name (Sysfs.list ()) then (
if Dhclient.is_running name then ignore (Dhclient.stop name) ;
Ip.flush_ip_addr name
)
()
| DHCP4 ->
let gateway =
Option.fold ~none:[]
~some:(fun n -> [`gateway n])
!config.gateway_interface
in
let dns =
Option.fold ~none:[]
~some:(fun n -> [`dns n])
!config.dns_interface
in
if not (Dhclient.is_running name) then (* Remove any static IPs *)
Ip.flush_ip_addr name ;
let options = gateway @ dns in
Dhclient.ensure_running name options
Dhclient.ensure_running name (config_to_dhcp_options !config)
| Static4 addrs ->
if Dhclient.is_running name then (
ignore (Dhclient.stop name) ;
Ip.flush_ip_addr name
) ;
(* the function is meant to be idempotent and we want to avoid
CA-239919 *)
let cur_addrs = Ip.get_ipv4 name in
Expand Down Expand Up @@ -569,53 +554,42 @@ module Interface = struct
else (
debug "Configuring IPv6 address for %s: %s" name
(conf |> Rpcmarshal.marshal typ_of_ipv6 |> Jsonrpc.to_string) ;
update_config name {(get_config name) with ipv6_conf= conf} ;
let previous_config = get_config name in
let previous = previous_config.ipv6_conf in
update_config name {previous_config with ipv6_conf= conf} ;
(* deconfigure previous *)
Xapi_stdext_pervasives.Pervasiveext.ignore_exn (fun () ->
match previous with
| None6 ->
()
| Linklocal6 ->
if conf <> Linklocal6 then Ip.flush_ip_addr ~ipv6:true name
| DHCP6 ->
if conf <> DHCP6 then Dhclient.stop ~ipv6:true name
| Autoconf6 ->
if conf <> Autoconf6 then Sysctl.set_ipv6_autoconf name false
| Static6 _ -> (
match conf with
| Static6 _ ->
()
| _ ->
Ip.flush_ip_addr ~ipv6:true name
)
) ;
(* configure conf *)
match conf with
| None6 ->
if List.mem name (Sysfs.list ()) then (
if Dhclient.is_running ~ipv6:true name then
ignore (Dhclient.stop ~ipv6:true name) ;
Sysctl.set_ipv6_autoconf name false ;
Ip.flush_ip_addr ~ipv6:true name
)
()
| Linklocal6 ->
if List.mem name (Sysfs.list ()) then (
if Dhclient.is_running ~ipv6:true name then
ignore (Dhclient.stop ~ipv6:true name) ;
Sysctl.set_ipv6_autoconf name false ;
Ip.flush_ip_addr ~ipv6:true name ;
Ip.set_ipv6_link_local_addr name
)
Ip.set_ipv6_link_local_addr name
| DHCP6 ->
let gateway =
Option.fold ~none:[]
~some:(fun n -> [`gateway n])
!config.gateway_interface
in
let dns =
Option.fold ~none:[]
~some:(fun n -> [`dns n])
!config.dns_interface
in
if Dhclient.is_running ~ipv6:true name then
ignore (Dhclient.stop ~ipv6:true name) ;
Sysctl.set_ipv6_autoconf name false ;
Ip.flush_ip_addr ~ipv6:true name ;
Ip.set_ipv6_link_local_addr name ;
let options = gateway @ dns in
ignore (Dhclient.ensure_running ~ipv6:true name options)
Dhclient.ensure_running ~ipv6:true name
(config_to_dhcp_options !config)
| Autoconf6 ->
if Dhclient.is_running ~ipv6:true name then
ignore (Dhclient.stop ~ipv6:true name) ;
Ip.flush_ip_addr ~ipv6:true name ;
Ip.set_ipv6_link_local_addr name ;
Sysctl.set_ipv6_autoconf name true
(* Cannot link set down/up due to CA-89882 - IPv4 default route
cleared *)
| Static6 addrs ->
if Dhclient.is_running ~ipv6:true name then
ignore (Dhclient.stop ~ipv6:true name) ;
Sysctl.set_ipv6_autoconf name false ;
(* add the link_local and clean the old one only when needed *)
let cur_addrs =
let addrs = Ip.get_ipv6 name in
Expand Down
105 changes: 68 additions & 37 deletions ocaml/networkd/lib/network_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -913,20 +913,20 @@ end
module Dhclient : sig
type interface = string

val remove_conf_file : ?ipv6:bool -> interface -> unit

val is_running : ?ipv6:bool -> interface -> bool

val stop : ?ipv6:bool -> interface -> unit
(** stop: stop the DHCP client managing [interface] if running and to unconfigure addresses. *)

val ensure_running :
?ipv6:bool
-> interface
-> [> `dns of string | `gateway of string] list
-> unit
(** ensure_running: ensure the DHCP client is up and running. On start, any previously assigned
addresses are discarded. *)
end = struct
type interface = string

(** pid_file: path to dhclient pidfile. *)
let pid_file ?(ipv6 = false) interface =
let ipv6' =
if ipv6 then
Expand All @@ -936,6 +936,7 @@ end = struct
in
Printf.sprintf "/var/run/dhclient%s-%s.pid" ipv6' interface

(** lease_file: path to dhclient lease file. *)
let lease_file ?(ipv6 = false) interface =
let ipv6' =
if ipv6 then
Expand All @@ -946,6 +947,7 @@ end = struct
Filename.concat "/var/lib/xcp"
(Printf.sprintf "dhclient%s-%s.leases" ipv6' interface)

(** conf_file: path of the dhclient configuration file. *)
let conf_file ?(ipv6 = false) interface =
let ipv6' =
if ipv6 then
Expand All @@ -956,6 +958,7 @@ end = struct
Filename.concat "/var/lib/xcp"
(Printf.sprintf "dhclient%s-%s.conf" ipv6' interface)

(** generate_conf: return the content of dhclient configuration file. *)
let[@warning "-27"] generate_conf ?(ipv6 = false) interface options =
let send = "host-name = gethostname()" in
let minimal =
Expand Down Expand Up @@ -993,21 +996,25 @@ end = struct
interface send
(String.concat ", " request)

(** read_conf_file: returns the content of dhclient configuration file. *)
let read_conf_file ?(ipv6 = false) interface =
let file = conf_file ~ipv6 interface in
try Some (Xapi_stdext_unix.Unixext.string_of_file file) with _ -> None

(** write_conf_file: write updated dhclient configuration file to disk. *)
let write_conf_file ?(ipv6 = false) interface options =
let conf = generate_conf ~ipv6 interface options in
Xapi_stdext_unix.Unixext.write_string_to_file
(conf_file ~ipv6 interface)
conf

(** remove_conf_file: unlink the dhclient configuration file from disk. *)
let remove_conf_file ?(ipv6 = false) interface =
let file = conf_file ~ipv6 interface in
try Unix.unlink file with _ -> ()

let start ?(ipv6 = false) interface options =
(** dhclient_start: configure and start dhclient. *)
let dhclient_start ~ipv6 interface options =
(* If we have a gateway interface, pass it to dhclient-script via -e *)
(* This prevents the default route being set erroneously on CentOS *)
(* Normally this wouldn't happen as we're not requesting routers, *)
Expand All @@ -1027,63 +1034,87 @@ end = struct
else
["-e"; "PEERDNS=no"]
in
write_conf_file ~ipv6 interface options ;
let ipv6' =
if ipv6 then
["-6"]
else
[]
in
call_script ~timeout:None dhclient
(ipv6'
@ gw_opt
@ dns_opt
@ [
"-q"
; "-pf"
; pid_file ~ipv6 interface
; "-lf"
; lease_file ~ipv6 interface
; "-cf"
; conf_file ~ipv6 interface
; interface
]
)

let stop ?(ipv6 = false) interface =
try
ignore
(call_script dhclient
[
"-r"
(* create an up-to-date configuration file. *)
write_conf_file ~ipv6 interface options ;
(* start dhclient *)
ignore
(call_script ~timeout:None dhclient
(ipv6'
@ gw_opt
@ dns_opt
@ [
"-q"
; "-pf"
; pid_file ~ipv6 interface
; "-lf"
; lease_file ~ipv6 interface
; "-cf"
; conf_file ~ipv6 interface
; interface
]
) ;
Unix.unlink (pid_file ~ipv6 interface)
with _ -> ()
)
)

(* dhclient_stop: stop the running dhclient. release lease if asked. *)
let dhclient_stop ~ipv6 ~release interface =
remove_conf_file ~ipv6 interface ;
ignore
(call_script dhclient
[
( if release then
"-r"
else
"-x"
)
; "-pf"
; pid_file ~ipv6 interface
; "-lf"
; lease_file ~ipv6 interface
; interface
]
)

let is_running ?(ipv6 = false) interface =
let start ?(ipv6 = false) interface options =
(* remove previously assigned addresses (like static ones) *)
Ip.flush_ip_addr interface ;
(* start DHCP client *)
dhclient_start ~ipv6 interface options

(* is_running: returns if the DHCP client is running. *)
let is_running ~ipv6 interface =
try
Unix.access (pid_file ~ipv6 interface) [Unix.F_OK] ;
true
with Unix.Unix_error _ -> false

let stop ?(ipv6 = false) interface =
if is_running ~ipv6 interface then
try
(* release DHCP lease and close the DHCP client *)
dhclient_stop ~ipv6 ~release:true interface ;
(* flush configured addresses *)
Ip.flush_ip_addr ~ipv6 interface ;
(* remove the pid file *)
Unix.unlink (pid_file ~ipv6 interface)
with _ -> ()

let ensure_running ?(ipv6 = false) interface options =
if not (is_running ~ipv6 interface) then
(* dhclient is not running, so we need to start it. *)
ignore (start ~ipv6 interface options)
start ~ipv6 interface options
else
(* dhclient is running - if the config has changed, update the config file
and restart. *)
(* dhclient is running, if the config has changed, reload it. *)
let current_conf = read_conf_file ~ipv6 interface in
let new_conf = generate_conf ~ipv6 interface options in
if current_conf <> Some new_conf then (
ignore (stop ~ipv6 interface) ;
ignore (start ~ipv6 interface options)
dhclient_stop ~ipv6 ~release:false interface ;
dhclient_start ~ipv6 interface options
)
end

Expand Down
Loading