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 |