;;======================================================================
;; network utilities
;;======================================================================
(module ulex-netutil
(get-all-ips get-my-best-address get-all-ips-sorted)
(import scheme chicken data-structures foreign ports regex-case posix)
;; #include <stdio.h>
;; #include <netinet/in.h>
;; #include <string.h>
;; #include <arpa/inet.h>
(foreign-declare "#include \"sys/types.h\"")
(foreign-declare "#include \"sys/socket.h\"")
(foreign-declare "#include \"ifaddrs.h\"")
(foreign-declare "#include \"arpa/inet.h\"")
;; get IP addresses from ALL interfaces
(define get-all-ips
(foreign-safe-lambda* scheme-object ()
"
// from https://stackoverflow.com/questions/17909401/linux-c-get-default-interfaces-ip-address :
C_word lst = C_SCHEME_END_OF_LIST, len, str, *a;
// struct ifaddrs *ifa, *i;
// struct sockaddr *sa;
struct ifaddrs * ifAddrStruct = NULL;
struct ifaddrs * ifa = NULL;
void * tmpAddrPtr = NULL;
if ( getifaddrs(&ifAddrStruct) != 0)
C_return(C_SCHEME_FALSE);
// for (i = ifa; i != NULL; i = i->ifa_next) {
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family==AF_INET) { // Check it is
// a valid IPv4 address
tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
// printf(\"%s IP Address %s\\n\", ifa->ifa_name, addressBuffer);
len = strlen(addressBuffer);
a = C_alloc(C_SIZEOF_PAIR + C_SIZEOF_STRING(len));
str = C_string(&a, len, addressBuffer);
lst = C_a_pair(&a, str, lst);
}
// else if (ifa->ifa_addr->sa_family==AF_INET6) { // Check it is
// // a valid IPv6 address
// tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
// char addressBuffer[INET6_ADDRSTRLEN];
// inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
//// printf(\"%s IP Address %s\\n\", ifa->ifa_name, addressBuffer);
// len = strlen(addressBuffer);
// a = C_alloc(C_SIZEOF_PAIR + C_SIZEOF_STRING(len));
// str = C_string(&a, len, addressBuffer);
// lst = C_a_pair(&a, str, lst);
// }
// else {
// printf(\" not an IPv4 address\\n\");
// }
}
freeifaddrs(ifa);
C_return(lst);
"))
;; Change this to bias for addresses with a reasonable broadcast value?
;;
(define (ip-pref-less? a b)
(let* ((rate (lambda (ipstr)
(regex-case ipstr
( "^127\\." _ 0 )
( "^(10\\.0|192\\.168\\.)\\..*" _ 1 )
( else 2 ) ))))
(< (rate a) (rate b))))
(define (get-my-best-address)
(let ((all-my-addresses (get-all-ips))
;;(all-my-addresses-old (vector->list (hostinfo-addresses (hostname->hostinfo (get-host-name)))))
)
(cond
((null? all-my-addresses)
(get-host-name)) ;; no interfaces?
((eq? (length all-my-addresses) 1)
(car all-my-addresses)) ;; only one to choose from, just go with it
(else
(car (sort all-my-addresses ip-pref-less?)))
;; (else
;; (ip->string (car (filter (lambda (x) ;; take any but 127.
;; (not (eq? (u8vector-ref x 0) 127)))
;; all-my-addresses))))
)))
(define (get-all-ips-sorted)
(sort (get-all-ips) ip-pref-less?))
)