diff --git a/ocaml/network/network_server.ml b/ocaml/network/network_server.ml index b92988c281e..42d0ea6ffbe 100644 --- a/ocaml/network/network_server.ml +++ b/ocaml/network/network_server.ml @@ -25,6 +25,7 @@ open D type context = unit let config : config_t ref = ref empty_config +let backend_kind = ref Openvswitch let legacy_management_interface_start () = try @@ -83,6 +84,13 @@ let set_dns_interface _ dbg ~name = debug "Setting DNS interface to %s" name; config := {!config with dns_interface = Some name} +(* The enic driver is for Cisco UCS devices. The current driver adds VLAN0 headers + * to all incoming packets, which confuses certain guests OSes. The workaround + * constitutes adding a VLAN0 Linux device to strip those headers again. + *) +let need_enic_workaround () = + !backend_kind = Bridge && List.mem "enic" (Sysfs.list_drivers ()) + module Interface = struct let get_config name = get_config !config.interface_config default_interface name @@ -355,6 +363,17 @@ module Interface = struct end else config in + let config = + if need_enic_workaround () then + List.fold_left (fun accu (name, interface) -> + if (Sysfs.is_physical name && Linux_bonding.get_bond_master_of name = None) || Linux_bonding.is_bond_device name then + (name, interface) :: (Ip.vlan_name name 0, interface) :: accu + else + (name, interface) :: accu + ) [] config + else + config + in debug "** Configuring the following interfaces: %s" (String.concat ", " (List.map (fun (name, _) -> name) config)); let exec f = if conservative then (try f () with _ -> ()) else f () in List.iter (function (name, ({ipv4_conf; ipv4_gateway; ipv6_conf; ipv6_gateway; ipv4_routes; dns=nameservers,domains; mtu; @@ -377,7 +396,6 @@ module Interface = struct end module Bridge = struct - let kind = ref Openvswitch let add_default = ref [] let get_config name = @@ -393,8 +411,8 @@ module Bridge = struct let backend = String.strip String.isspace (Unixext.string_of_file (Fhs.etcdir ^ "/network.conf")) in match backend with - | "openvswitch" | "vswitch" -> kind := Openvswitch - | "bridge" -> kind := Bridge + | "openvswitch" | "vswitch" -> backend_kind := Openvswitch + | "bridge" -> backend_kind := Bridge | backend -> let error = Printf.sprintf "ERROR: network backend unknown (%s)" backend in debug "%s" error; @@ -402,14 +420,14 @@ module Bridge = struct let get_bond_links_up _ dbg ~name = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.get_bond_links_up name | Bridge -> Proc.get_bond_links_up name ) () let get_all _ dbg () = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.list_bridges () | Bridge -> Sysfs.get_all_bridges () ) () @@ -421,7 +439,7 @@ module Bridge = struct | Some (parent, vlan) -> Printf.sprintf " (VLAN %d on bridge %s)" vlan parent ); update_config name {get_config name with vlan; bridge_mac=mac; other_config}; - begin match !kind with + begin match !backend_kind with | Openvswitch -> let fail_mode = if not (List.mem_assoc "vswitch-controller-fail-mode" other_config) then @@ -496,7 +514,7 @@ module Bridge = struct let destroy _ dbg ?(force=false) ~name () = Debug.with_thread_associated dbg (fun () -> Interface.bring_down () dbg ~name; - match !kind with + match !backend_kind with | Openvswitch -> if Ovs.get_vlans name = [] || force then begin debug "Destroying bridge %s" name; @@ -529,8 +547,14 @@ module Bridge = struct Interface.bring_down () dbg ~name:dev; if Linux_bonding.is_bond_device dev then Linux_bonding.remove_bond_master dev; - if String.startswith "eth" dev && String.contains dev '.' then - ignore (Ip.destroy_vlan dev) + if (String.startswith "eth" dev || String.startswith "bond" dev) && String.contains dev '.' then begin + ignore (Ip.destroy_vlan dev); + let n = String.length dev in + if String.sub dev (n - 2) 2 = ".0" && need_enic_workaround () then + let vlan_base = String.sub dev 0 (n - 2) in + if Linux_bonding.is_bond_device vlan_base then + Linux_bonding.remove_bond_master (String.sub dev 0 (n - 2)) + end; ) ifs; Interface.set_ipv4_conf () dbg ~name ~conf:None4; ignore (Brctl.destroy_bridge name) @@ -540,12 +564,12 @@ module Bridge = struct let get_kind _ dbg () = Debug.with_thread_associated dbg (fun () -> - !kind + !backend_kind ) () let get_ports _ dbg ~name = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.bridge_to_ports name | Bridge -> raise Not_implemented ) () @@ -556,14 +580,14 @@ module Bridge = struct let ports = List.concat (List.map (fun (_, {ports}) -> ports) !config.bridge_config) in List.map (fun (port, {interfaces}) -> port, interfaces) ports else - match !kind with + match !backend_kind with | Openvswitch -> List.concat (List.map Ovs.bridge_to_ports (Ovs.list_bridges ())) | Bridge -> raise Not_implemented ) () let get_bonds _ dbg ~name = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.bridge_to_ports name | Bridge -> raise Not_implemented ) () @@ -575,21 +599,21 @@ module Bridge = struct let names = List.map (fun (port, {interfaces}) -> port, interfaces) ports in List.filter (fun (_, ifs) -> List.length ifs > 1) names else - match !kind with + match !backend_kind with | Openvswitch -> List.concat (List.map Ovs.bridge_to_ports (Ovs.list_bridges ())) | Bridge -> raise Not_implemented ) () let get_vlan _ dbg ~name = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.bridge_to_vlan name | Bridge -> raise Not_implemented ) () let add_default_flows _ dbg bridge mac interfaces = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.add_default_flows bridge mac interfaces | Bridge -> () ) () @@ -608,7 +632,7 @@ module Bridge = struct debug "Adding port %s to bridge %s with interfaces %s%s" name bridge (String.concat ", " interfaces) (match bond_mac with Some mac -> " and MAC " ^ mac | None -> ""); - match !kind with + match !backend_kind with | Openvswitch -> if List.length interfaces = 1 then begin List.iter (fun name -> Interface.bring_up () dbg ~name) interfaces; @@ -633,10 +657,9 @@ module Bridge = struct name bridge end | Bridge -> - if List.length interfaces = 1 then begin - List.iter (fun name -> Interface.bring_up () dbg ~name) interfaces; - ignore (Brctl.create_port bridge name) - end else begin + if List.length interfaces = 1 then + List.iter (fun name -> Interface.bring_up () dbg ~name) interfaces + else begin if not (List.mem name (Sysfs.bridge_to_interfaces bridge)) then begin Linux_bonding.add_bond_master name; let bond_properties = @@ -652,9 +675,16 @@ module Bridge = struct | None -> warn "No MAC address specified for the bond" end end; - Interface.bring_up () dbg ~name; + Interface.bring_up () dbg ~name + end; + if need_enic_workaround () then begin + debug "Applying enic workaround: adding VLAN0 device to bridge"; + Ip.create_vlan name 0; + let vlan0 = Ip.vlan_name name 0 in + Interface.bring_up () dbg ~name:vlan0; + ignore (Brctl.create_port bridge vlan0) + end else ignore (Brctl.create_port bridge name) - end ) () let remove_port _ dbg ~bridge ~name = @@ -665,7 +695,7 @@ module Bridge = struct let ports = List.remove_assoc name config.ports in update_config bridge {config with ports} end; - match !kind with + match !backend_kind with | Openvswitch -> ignore (Ovs.destroy_port name) | Bridge -> @@ -674,7 +704,7 @@ module Bridge = struct let get_interfaces _ dbg ~name = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> Ovs.bridge_to_interfaces name | Bridge -> @@ -683,7 +713,7 @@ module Bridge = struct let get_fail_mode _ dbg ~name = Debug.with_thread_associated dbg (fun () -> - match !kind with + match !backend_kind with | Openvswitch -> begin match Ovs.get_fail_mode name with | "standalone" -> Some Standalone @@ -766,7 +796,7 @@ let on_startup () = (* the following is best-effort *) read_config (); remove_centos_config (); - if !Bridge.kind = Openvswitch then + if !backend_kind = Openvswitch then Ovs.set_max_idle 5000; Bridge.make_config () dbg ~conservative:true ~config:!config.bridge_config (); Interface.make_config () dbg ~conservative:true ~config:!config.interface_config (); diff --git a/ocaml/network/network_utils.ml b/ocaml/network/network_utils.ml index 3be2cbe5d0b..095e129c65f 100644 --- a/ocaml/network/network_utils.ml +++ b/ocaml/network/network_utils.ml @@ -59,6 +59,13 @@ module Sysfs = struct let all = Array.to_list (Sys.readdir "/sys/class/net") in List.filter (fun name -> Sys.is_directory ("/sys/class/net/" ^ name)) all + let list_drivers () = + try + Array.to_list (Sys.readdir "/sys/bus/pci/drivers") + with _ -> + warn "Failed to obtain list of drivers from sysfs"; + [] + let getpath dev attr = Printf.sprintf "/sys/class/net/%s/%s" dev attr