syscalls: Add 'network-interfaces', which wraps libc's 'getifaddrs'.
Based on discussions with Rohan Prinja <rohan.prinja@gmail.com>. * guix/build/syscalls.scm (<interface>): New record type. (write-interface, values->interface, unfold-interface-list, network-interfaces, free-ifaddrs): New procedures. (ifaddrs): New C struct. (%struct-ifaddrs-type, %sizeof-ifaddrs): New macros. * tests/syscalls.scm ("network-interfaces returns one or more interfaces", "network-interfaces returns \"lo\""): New tests.
This commit is contained in:
parent
573b4c1ff3
commit
e7f5691d45
|
@ -21,6 +21,8 @@
|
||||||
#:use-module (system foreign)
|
#:use-module (system foreign)
|
||||||
#:use-module (rnrs bytevectors)
|
#:use-module (rnrs bytevectors)
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
|
#:use-module (srfi srfi-9)
|
||||||
|
#:use-module (srfi srfi-9 gnu)
|
||||||
#:use-module (ice-9 rdelim)
|
#:use-module (ice-9 rdelim)
|
||||||
#:use-module (ice-9 regex)
|
#:use-module (ice-9 regex)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
@ -70,7 +72,15 @@
|
||||||
set-network-interface-flags
|
set-network-interface-flags
|
||||||
set-network-interface-address
|
set-network-interface-address
|
||||||
set-network-interface-up
|
set-network-interface-up
|
||||||
configure-network-interface))
|
configure-network-interface
|
||||||
|
|
||||||
|
interface?
|
||||||
|
interface-name
|
||||||
|
interface-flags
|
||||||
|
interface-address
|
||||||
|
interface-netmask
|
||||||
|
interface-broadcast-address
|
||||||
|
network-interfaces))
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
;;;
|
;;;
|
||||||
|
@ -713,4 +723,108 @@ the same type as that returned by 'make-socket-address'."
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(close-port sock)))))
|
(close-port sock)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Details about network interfaces---aka. 'getifaddrs'.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;; Network interfaces. XXX: We would call it <network-interface> but that
|
||||||
|
;; would collide with the ioctl wrappers above.
|
||||||
|
(define-record-type <interface>
|
||||||
|
(make-interface name flags address netmask broadcast-address)
|
||||||
|
interface?
|
||||||
|
(name interface-name) ;string
|
||||||
|
(flags interface-flags) ;or'd IFF_* values
|
||||||
|
(address interface-address) ;sockaddr | #f
|
||||||
|
(netmask interface-netmask) ;sockaddr | #f
|
||||||
|
(broadcast-address interface-broadcast-address)) ;sockaddr | #f
|
||||||
|
|
||||||
|
(define (write-interface interface port)
|
||||||
|
(match interface
|
||||||
|
(($ <interface> name flags address)
|
||||||
|
(format port "#<interface ~s " name)
|
||||||
|
(unless (zero? (logand IFF_UP flags))
|
||||||
|
(display "up " port))
|
||||||
|
(if (member (sockaddr:fam address) (list AF_INET AF_INET6))
|
||||||
|
(format port "~a " (inet-ntop (sockaddr:fam address)
|
||||||
|
(sockaddr:addr address)))
|
||||||
|
(format port "family:~a " (sockaddr:fam address)))
|
||||||
|
(format port "~a>" (number->string (object-address interface) 16)))))
|
||||||
|
|
||||||
|
(set-record-type-printer! <interface> write-interface)
|
||||||
|
|
||||||
|
(define (values->interface next name flags address netmask
|
||||||
|
broadcast-address data)
|
||||||
|
"Given the raw field values passed as arguments, return a pair whose car is
|
||||||
|
an <interface> object, and whose cdr is the pointer NEXT."
|
||||||
|
(define (maybe-socket-address pointer)
|
||||||
|
(if (null-pointer? pointer)
|
||||||
|
#f
|
||||||
|
(read-socket-address (pointer->bytevector pointer 50)))) ;XXX: size
|
||||||
|
|
||||||
|
(cons (make-interface (if (null-pointer? name)
|
||||||
|
#f
|
||||||
|
(pointer->string name))
|
||||||
|
flags
|
||||||
|
(maybe-socket-address address)
|
||||||
|
(maybe-socket-address netmask)
|
||||||
|
(maybe-socket-address broadcast-address)
|
||||||
|
;; Ignore DATA.
|
||||||
|
)
|
||||||
|
next))
|
||||||
|
|
||||||
|
(define-c-struct ifaddrs ;<ifaddrs.h>
|
||||||
|
values->interface
|
||||||
|
read-ifaddrs
|
||||||
|
write-ifaddrs!
|
||||||
|
(next '*)
|
||||||
|
(name '*)
|
||||||
|
(flags unsigned-int)
|
||||||
|
(addr '*)
|
||||||
|
(netmask '*)
|
||||||
|
(broadcastaddr '*)
|
||||||
|
(data '*))
|
||||||
|
|
||||||
|
(define-syntax %struct-ifaddrs-type
|
||||||
|
(identifier-syntax
|
||||||
|
`(* * ,unsigned-int * * * *)))
|
||||||
|
|
||||||
|
(define-syntax %sizeof-ifaddrs
|
||||||
|
(identifier-syntax
|
||||||
|
(sizeof* %struct-ifaddrs-type)))
|
||||||
|
|
||||||
|
(define (unfold-interface-list ptr)
|
||||||
|
"Call 'read-ifaddrs' on PTR and all its 'next' fields, recursively, and
|
||||||
|
return the list of resulting <interface> objects."
|
||||||
|
(let loop ((ptr ptr)
|
||||||
|
(result '()))
|
||||||
|
(if (null-pointer? ptr)
|
||||||
|
(reverse result)
|
||||||
|
(match (read-ifaddrs (pointer->bytevector ptr %sizeof-ifaddrs)
|
||||||
|
0)
|
||||||
|
((ifaddr . ptr)
|
||||||
|
(loop ptr (cons ifaddr result)))))))
|
||||||
|
|
||||||
|
(define network-interfaces
|
||||||
|
(let* ((ptr (dynamic-func "getifaddrs" (dynamic-link)))
|
||||||
|
(proc (pointer->procedure int ptr (list '*))))
|
||||||
|
(lambda ()
|
||||||
|
"Return a list of <interface> objects, each denoting a configured
|
||||||
|
network interface. This is implemented using the 'getifaddrs' libc function."
|
||||||
|
(let* ((ptr (bytevector->pointer (make-bytevector (sizeof* '*))))
|
||||||
|
(ret (proc ptr))
|
||||||
|
(err (errno)))
|
||||||
|
(if (zero? ret)
|
||||||
|
(let* ((ptr (dereference-pointer ptr))
|
||||||
|
(result (unfold-interface-list ptr)))
|
||||||
|
(free-ifaddrs ptr)
|
||||||
|
result)
|
||||||
|
(throw 'system-error "network-interfaces" "~A"
|
||||||
|
(list (strerror err))
|
||||||
|
(list err)))))))
|
||||||
|
|
||||||
|
(define free-ifaddrs
|
||||||
|
(let ((ptr (dynamic-func "freeifaddrs" (dynamic-link))))
|
||||||
|
(pointer->procedure void ptr '(*))))
|
||||||
|
|
||||||
;;; syscalls.scm ends here
|
;;; syscalls.scm ends here
|
||||||
|
|
|
@ -211,6 +211,29 @@
|
||||||
;; We get EPERM with Linux 3.18ish and EACCES with 2.6.32.
|
;; We get EPERM with Linux 3.18ish and EACCES with 2.6.32.
|
||||||
(memv (system-error-errno args) (list EPERM EACCES))))))
|
(memv (system-error-errno args) (list EPERM EACCES))))))
|
||||||
|
|
||||||
|
(test-equal "network-interfaces returns one or more interfaces"
|
||||||
|
'(#t #t #t)
|
||||||
|
(match (network-interfaces)
|
||||||
|
((interfaces ..1)
|
||||||
|
(list (every interface? interfaces)
|
||||||
|
(every string? (map interface-name interfaces))
|
||||||
|
(every vector? (map interface-address interfaces))))))
|
||||||
|
|
||||||
|
(test-equal "network-interfaces returns \"lo\""
|
||||||
|
(list #t (make-socket-address AF_INET (inet-pton AF_INET "127.0.0.1") 0))
|
||||||
|
(match (filter (lambda (interface)
|
||||||
|
(string=? "lo" (interface-name interface)))
|
||||||
|
(network-interfaces))
|
||||||
|
((loopbacks ..1)
|
||||||
|
(list (every (lambda (lo)
|
||||||
|
(not (zero? (logand IFF_LOOPBACK (interface-flags lo)))))
|
||||||
|
loopbacks)
|
||||||
|
(match (find (lambda (lo)
|
||||||
|
(= AF_INET (sockaddr:fam (interface-address lo))))
|
||||||
|
loopbacks)
|
||||||
|
(#f #f)
|
||||||
|
(lo (interface-address lo)))))))
|
||||||
|
|
||||||
(test-end)
|
(test-end)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue