guile-netlink/netlink/connection.scm

connection.scm

1
;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu>
2
;;;; 
3
;;;; This library is free software; you can redistribute it and/or
4
;;;; modify it under the terms of the GNU Lesser General Public
5
;;;; License as published by the Free Software Foundation; either
6
;;;; version 3 of the License, or (at your option) any later version.
7
;;;; 
8
;;;; This library is distributed in the hope that it will be useful,
9
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
;;;; Lesser General Public License for more details.
12
;;;; 
13
;;;; You should have received a copy of the GNU Lesser General Public
14
;;;; License along with this library; if not, write to the Free Software
15
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
;;;; 
17
18
(define-module (netlink connection)
19
  #:use-module (netlink constant)
20
  #:use-module (netlink data)
21
  #:use-module (netlink message)
22
  #:use-module (rnrs bytevectors)
23
  #:use-module (system foreign)
24
  #:use-module (srfi srfi-9)
25
  #:export (connect
26
            connect-route
27
            close-socket
28
            send-msg
29
            receive-msg
30
            receive-and-decode-msg
31
            get-addr))
32
33
(define libc (dynamic-link))
34
(define ffi-socket (pointer->procedure int
35
                                       (dynamic-func "socket" libc)
36
                                       (list int int int)))
37
(define ffi-close (pointer->procedure void
38
                                      (dynamic-func "close" libc)
39
                                      (list int)))
40
(define ffi-sendmsg (pointer->procedure int
41
                                        (dynamic-func "sendmsg" libc)
42
                                        (list int '* int)
43
                                        #:return-errno? #t))
44
(define ffi-sendto (pointer->procedure int
45
                                       (dynamic-func "sendto" libc)
46
                                       (list int '* size_t int '* int)
47
                                       #:return-errno? #t))
48
(define ffi-recvmsg (pointer->procedure int
49
                                        (dynamic-func "recvmsg" libc)
50
                                        (list int '* int)))
51
(define ffi-bind (pointer->procedure int
52
                                     (dynamic-func "bind" libc)
53
                                     (list int '* int)))
54
55
;; define socket type
56
(define-record-type socket
57
    (make-socket num open?)
58
    socket?
59
    (num socket-num)
60
    (open? socket-open?))
61
62
;; define simple functions to open/close sockets
63
(define (open-socket proto)
64
    (make-socket (ffi-socket AF_NETLINK (logior SOCK_RAW SOCK_CLOEXEC) proto) #t))
65
(define (close-socket socket)
66
    (if (socket-open? socket)
67
        (ffi-close (socket-num socket)))
68
    (make-socket (socket-num socket) #f))
69
70
(define (get-addr family pid groups)
71
  (let ((addr (make-bytevector 12)))
72
    (bytevector-u16-set! addr 0 family (native-endianness))
73
    (bytevector-u32-set! addr 4 pid (native-endianness))
74
    (bytevector-u32-set! addr 8 groups (native-endianness))
75
    addr))
76
77
(define (get-msghdr name namelen iov iovlen control controllen flags)
78
  (make-c-struct
79
    (list '* size_t '* size_t '* size_t int)
80
    (list name namelen iov iovlen control controllen flags)))
81
82
(define (get-iovec content size)
83
  (make-c-struct
84
    (list '* size_t)
85
    (list content size)))
86
87
(define* (connect proto addr)
88
  (let ((sock (open-socket proto)))
89
    (ffi-bind (socket-num sock)
90
              (bytevector->pointer addr)
91
              12)
92
    sock))
93
94
(define* (connect-route #:key (groups 0))
95
  (connect NETLINK_ROUTE (get-addr AF_NETLINK 0 groups)))
96
97
(define* (send-msg msg sock #:key (addr (get-addr AF_NETLINK 0 0)))
98
  (unless (message? msg)
99
    (throw 'cannot-send-not-message-type))
100
101
  (let* ((len (data-size msg))
102
         (bv (make-bytevector len)))
103
    (serialize msg 0 bv)
104
    (ffi-sendto (socket-num sock) (bytevector->pointer bv) len 0 %null-pointer 0)))
105
106
(define* (receive-msg sock #:key (addr (get-addr AF_NETLINK 0 0)))
107
  (let* ((len (* 1024 32))
108
         (bv (make-bytevector len))
109
         (iovec (get-iovec (bytevector->pointer bv) len))
110
         (msghdr (get-msghdr (bytevector->pointer addr) (bytevector-length addr)
111
                             iovec 1
112
                             %null-pointer 0
113
                             0))
114
         (size (ffi-recvmsg (socket-num sock) msghdr 0))
115
         (answer (make-bytevector size)))
116
    (when (> size (* 1024 32))
117
      (throw 'answer-too-big))
118
    (when (> size 0)
119
      (bytevector-copy! bv 0 answer 0 size))
120
    answer))
121
122
(define* (receive-and-decode-msg sock decoder
123
                                 #:key (addr (get-addr AF_NETLINK 0 0)))
124
  (let* ((answer (receive-msg sock #:addr addr))
125
         (size (bytevector-length answer)))
126
    (let loop ((messages '()) (pos 0))
127
      (if (>= pos size)
128
          (let ((last-message (car messages)))
129
            (if (and
130
                  (equal? (logand (message-flags last-message) NLM_F_MULTI)
131
                          NLM_F_MULTI)
132
                  (> (message-kind last-message) NLMSG_OVERUN))
133
                (append (reverse messages)
134
                        (receive-and-decode-msg sock decoder #:addr addr))
135
                (reverse messages)))
136
          (let ((message (deserialize 'message decoder answer pos)))
137
            (loop (cons message messages)
138
                  (+ (data-size message) pos)))))))
139