File: netdev.ml

package info (click to toggle)
xen-api-libs 0.5.2-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,940 kB
  • sloc: ml: 13,925; sh: 2,930; ansic: 1,699; makefile: 1,240; python: 83
file content (440 lines) | stat: -rw-r--r-- 12,041 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
(*
 * Copyright (C) 2006-2009 Citrix Systems Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *)
open Stringext
open Forkhelpers

type kind = Bridge | Vswitch

type network_ops = { 
  kind: kind;
  add: string -> ?uuid:string -> unit;
  del: string -> unit;
  list: unit -> string list;

  exists: string -> bool;

  intf_add: string -> string -> unit;
  intf_del: string -> string -> unit;
  intf_list: string -> string list;

  get_bridge: string -> string;
  is_on_bridge: string -> bool;

  set_forward_delay: string -> int -> unit;
}

exception Unknown_network_backend of string
exception Invalid_network_backend_operation of string * kind

let string_of_kind kind = match kind with
  | Bridge -> "bridge"
  | Vswitch -> "openvswitch"

let kind_of_string s = match s with
  | "bridge" -> Bridge
  | "vswitch" -> Vswitch
  | "openvswitch" -> Vswitch
  | _ -> raise (Unknown_network_backend s)

module Internal = struct

let control_socket () =
	try
		Unix.socket Unix.PF_INET Unix.SOCK_DGRAM 0
	with
	exn ->
		try
			Unix.socket Unix.PF_UNIX Unix.SOCK_DGRAM 0
		with
		exn ->
			Unix.socket Unix.PF_INET6 Unix.SOCK_DGRAM 0

let with_fd f =
	let fd = control_socket () in
	let r = begin try
		f fd
	with
	exn ->
		Unix.close fd;
		raise exn
	end in
	Unix.close fd;
	r

let exec cmd =
	let ret = Sys.command cmd in
	if ret <> 0 then
		failwith (Printf.sprintf "cmd returned %d" ret)

let read_one_line file =
	let inchan = open_in file in
	try
		let result = input_line inchan in
		close_in inchan;
		result
	with exn -> close_in inchan; raise exn

let write_one_line file l =
	let outchan = open_out file in
	try
		output_string outchan (l ^ "\n");
		close_out outchan
	with
		exn -> close_out outchan; raise exn
end

module Bridge = struct

external _add : Unix.file_descr -> string -> unit = "stub_bridge_add"
external _del : Unix.file_descr -> string -> unit = "stub_bridge_del"

let add name ?uuid = 
	Internal.with_fd (fun fd -> _add fd name)

let del name =
	Internal.with_fd (fun fd -> _del fd name)

let list () =
	let dirs = Array.to_list (Sys.readdir "/sys/class/net") in
	List.filter (fun dir ->
		Sys.file_exists ("/sys/class/net/" ^ dir ^ "/bridge")) dirs

let exists name =
	try Sys.file_exists ("/sys/class/net/" ^ name ^ "/bridge")
	with _ -> false

let set name obj v =
	let file = "/sys/class/net/" ^ name ^ "/bridge/" ^ obj in
	let outchan = open_out file in
	output_string outchan v;
	output_char outchan '\n';
	close_out outchan

let get name obj = Internal.read_one_line ("/sys/class/net/" ^ name ^ "/bridge/" ^ obj) 
	  
let _forward_delay = "forward_delay"
let _hello_time = "hello_time"
let _max_age = "max_age"
let _ageing_time = "ageing_time"
let _stp_state = "stp_state"
let _priority = "priority"
let _bridge_id = "bridge_id"

let get_id name = 
	get name _bridge_id

let set_forward_delay name v =
	set name _forward_delay (string_of_int v)

let get_forward_delay name =
	int_of_string (get name _forward_delay)

let set_hello_time name v =
	set name _hello_time (string_of_int v)

let get_hello_time name =
	int_of_string (get name _hello_time)

let set_max_age name v =
	set name _max_age (string_of_int v)

let get_max_age name = 
	int_of_string (get name _max_age)

let set_ageing_time name v =
	set name _ageing_time (string_of_int v)

let get_ageing_time name = 
	int_of_string (get name _ageing_time)

let set_stp_state name v =
	set name _stp_state (if v then "1" else "0")

let get_stp_state name = 
	get name _stp_state <> "0"

let set_priority name v =
	set name _priority (string_of_int v)

let get_priority name = 
	int_of_string (get name _priority)

(* bridge interfaces control function *)
external _intf_add : Unix.file_descr -> string -> string -> unit
                   = "stub_bridge_intf_add"
external _intf_del : Unix.file_descr -> string -> string -> unit
                   = "stub_bridge_intf_del"

let intf_add name intf =
	Internal.with_fd (fun fd -> _intf_add fd name intf)

let intf_del name intf =
	Internal.with_fd (fun fd -> _intf_del fd name intf)

let intf_list name =
	Array.to_list (Sys.readdir ("/sys/class/net/" ^ name ^ "/brif/"))

let getpath dev attr = Printf.sprintf "/sys/class/net/%s/%s" dev attr

let is_on_bridge name = try Unix.access (getpath name "brport") [ Unix.F_OK ]; true with _ -> false

let get_bridge name = Filename.basename (Unix.readlink ((getpath name "brport") ^ "/bridge"))

let ops = {
  kind = Bridge;

  add = add;
  del = del;
  list = list;

  exists = exists;

  intf_add = intf_add;
  intf_del = intf_del;
  intf_list = intf_list;

  get_bridge = get_bridge;
  is_on_bridge = is_on_bridge;

  set_forward_delay = set_forward_delay;
}

end

module Vswitch = struct

let vsctl_script = "/usr/bin/ovs-vsctl"

let vsctl args =
  Unix.access vsctl_script [ Unix.X_OK ];
  let output, _ = Forkhelpers.execute_command_get_output vsctl_script args in
  let stripped = Stringext.String.strip (fun c -> c='\n') output in
  match stripped with
    | "" -> []
    | s -> Stringext.String.split '\n' s

let add name ?uuid = 
  let extra = match uuid with
    | Some uuid' -> ["--"; "br-set-external-id"; name; "xs-network-uuids"; uuid']
    | None -> ["--"; "foo"] in
  ignore(vsctl (["add-br" ; name] @ extra))
let del name = ignore(vsctl ["del-br" ; name])
let list () = vsctl [ "list-br" ]

let exists name = List.exists (fun x -> x = name) (list ())

let intf_add name intf = ignore(vsctl ["add-port"; name; intf])
let intf_del name intf = ignore(vsctl ["del-port"; name; intf])
let intf_list name = vsctl [ "list-ports"; name ]

let get_bridge name = 
  match vsctl [ "port-to-br"; name ] with
  | l::[] -> l
  | [] -> failwith ("ovs-vsctl port-to-br: did not return a bridge for port " ^ name)
  | _ -> failwith ("ovs-vsctl port-to-br: returned an unexpected number of results for port " ^ name)

let is_on_bridge name = 
  match vsctl [ "port-to-br"; name ] with
  | l::[] -> true
  | [] -> false
  | _ -> failwith ("ovs-vsctl port-to-br: returned an unexpected number of results for port " ^ name)

let ops = {
  kind = Vswitch;

  add = add;
  del = del;
  list = list;

  exists = exists;

  intf_add = intf_add;
  intf_del = intf_del;
  intf_list = intf_list;

  get_bridge = get_bridge;
  is_on_bridge = is_on_bridge;

  set_forward_delay = fun name v -> raise (Invalid_network_backend_operation ("set_forward_delay", Vswitch))
}

end

module Link = struct

type speed = int (* see CA-24610 *)
type duplex = Duplex_unknown | Duplex_half | Duplex_full

let string_of_duplex = function
	| Duplex_unknown -> "unknown"
	| Duplex_half    -> "half"
	| Duplex_full    -> "full"

let duplex_of_string = function
	| "full"    -> Duplex_full
	| "half"    -> Duplex_half
	| _         -> Duplex_unknown

let int_of_speed x = x
let speed_of_int x = x
let speed_unknown = 0

external _up : Unix.file_descr -> string -> unit = "stub_link_up"
external _is_up : Unix.file_descr -> string -> bool = "stub_link_is_up"
external _down : Unix.file_descr -> string -> unit = "stub_link_down"
external _multicast : Unix.file_descr -> string -> bool -> unit = "stub_link_multicast"
external _arp : Unix.file_descr -> string -> bool -> unit = "stub_link_arp"
external _change_name : Unix.file_descr -> string -> string -> unit = "stub_link_change_name"
external _get_status : Unix.file_descr -> string -> speed * duplex = "stub_link_get_status"

let up name =
	Internal.with_fd (fun fd -> _up fd name)

let is_up name =
	Internal.with_fd (fun fd -> try _is_up fd name with _ -> false)

let down name =
	Internal.with_fd (fun fd -> _down fd name)

let multicast name v =
	Internal.with_fd (fun fd -> _multicast fd name v)

let arp name v =
	Internal.with_fd (fun fd -> _arp fd name v)

let change_name name newname =
	Internal.with_fd (fun fd -> _change_name fd name newname)

let set_addr name addr =
	(* temporary *)
	Internal.exec (Printf.sprintf "ip link set %s addr %s" name addr)

let get_status name =
	Internal.with_fd (fun fd -> _get_status fd name)

end

module Addr = struct

let flush name =
	Internal.exec (Printf.sprintf "ip addr flush %s" name)

external __get_all : unit -> (string * string * string * bool) list = "stub_if_getaddr"

type addr = IPV4 of string * string | IPV6 of string * string

let get_all () =
	List.map (fun (name, addr, netmask, inet6) -> name, if inet6 then IPV6 (addr,netmask) else IPV4 (addr,netmask))
	         (__get_all ())

let get_all_ipv4 () =
	let ipv4s = List.filter (fun (_, _, _, inet6) -> not inet6) (__get_all ()) in
	List.map (fun (name, addr, netmask, _) ->
		name, Unix.inet_addr_of_string addr, Unix.inet_addr_of_string netmask
		) ipv4s

let get name =
	List.map (fun (a,b,c) -> (b,c)) (List.filter (fun (dev, _, _) -> dev = name) (get_all_ipv4 ()))

end

let list () =
	Array.to_list (Sys.readdir "/sys/class/net")

let getpath dev attr = Printf.sprintf "/sys/class/net/%s/%s" dev attr

let get_address name =
	try
		let master_path = Unix.readlink (getpath name "master") in
		let master = List.hd (List.rev (String.split '/' master_path)) in
		let proc ac line =
			try
				let a = String.index line ':' in
				let k = String.sub line 0 a in
				let v = String.sub_to_end line (a + 2) in
				if k = "Slave Interface" && v = name then
					Some ""
				else if ac = Some "" && k = "Permanent HW addr" then
					Some v
				else
					ac
			with _ -> ac
		in
		match Unixext.file_lines_fold proc None ("/proc/net/bonding/" ^ master) with
		| None -> raise Not_found
		| Some address -> address
	with _ -> 
		Internal.read_one_line (getpath name "address")

let get_mtu name = Internal.read_one_line (getpath name "mtu")
let set_mtu name mtu =
	Internal.write_one_line (getpath name "mtu")
	                        (string_of_int mtu)

let get_by_address address = 
  List.filter
    (fun device ->
       (* CA-21402: Not everything returned by list() is guaranteed to be a directory containing an address;
	  so we have to make sure we catch exceptions here so we keep trying the next one and so on.. *)
       try String.lowercase (get_address device) = String.lowercase address with _ -> false)
    (list ()) 
  
let get_pcibuspath name =
	try
		let devpath = Unix.readlink (getpath name "device") in
		List.hd (List.rev (String.split '/' devpath))
	with exn -> "N/A"

let get_carrier name =
	let i = int_of_string (Internal.read_one_line (getpath name "carrier")) in
	match i with 1 -> true | 0 -> false | _ -> false

let get_ids name =
	let read_id_from path =
		try
			let l = Internal.read_one_line path in
			(* trim 0x *)
			String.sub l 2 (String.length l - 2)
		with _ -> ""
		in
	read_id_from (getpath name "device/vendor"),
	read_id_from (getpath name "device/device")

let is_physical name = 
  try 
	let link = Unix.readlink (getpath name "device") in
	(* filter out device symlinks which look like /../../../devices/xen-backend/vif- *)
	not(List.mem "xen-backend" (String.split '/' link))
  with _ -> false

let get_bios_name name =
	try
		let output, _ = Forkhelpers.execute_command_get_output "/sbin/biosdevname" ["-i"; name] in
		let output = String.strip String.isspace output in
		output
	with _ -> name

(* Dispatch network backend operations. *)

let network_config_file = "/etc/xcp/network.conf"
let network_backend = 
  try 
    kind_of_string (String.strip String.isspace (Unixext.string_of_file network_config_file))
  with
  | Unix.Unix_error(Unix.ENOENT, "open", _) -> Bridge
  | Unix.Unix_error(err, op, path) -> failwith (Printf.sprintf "Unix error: %s (%s,%s)\n" (Unix.error_message err) op path)

let network = match network_backend with
  | Bridge -> Bridge.ops
  | Vswitch -> Vswitch.ops