guile-netlink/ip/link.scm

link.scm

1
;;;; Copyright (C) 2021 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 (ip link)
19
  #:use-module (ice-9 match)
20
  #:use-module (netlink route attrs)
21
  #:use-module (netlink route link)
22
  #:use-module (netlink connection)
23
  #:use-module (netlink constant)
24
  #:use-module (netlink data)
25
  #:use-module (netlink deserialize)
26
  #:use-module (netlink message)
27
  #:use-module (netlink standard)
28
  #:use-module (srfi srfi-1)
29
  #:use-module (srfi srfi-9)
30
  #:export (link-add
31
            link-del
32
            link-set
33
            link-show))
34
35
(define-record-type <link>
36
  (make-link name id type flags mtu qdisc state mode group qlen addr brd)
37
  link?
38
  (name  link-name)
39
  (id    link-id)
40
  (type  link-type)
41
  (flags link-flags)
42
  (mtu   link-mtu)
43
  (qdisc link-qdisc)
44
  (state link-state)
45
  (mode  link-mode)
46
  (group link-group)
47
  (qlen  link-qlen)
48
  (addr  link-addr)
49
  (brd   link-brd))
50
51
(define (get-attr attrs type)
52
  (let ((attrs (filter (lambda (attr) (equal? (route-attr-kind attr) type)) attrs)))
53
    (match attrs
54
      (() #f)
55
      ((attr) (nl-data-data (route-attr-data attr))))))
56
57
(define (split-flags flags)
58
  (let loop ((max-flag 262144) (flags flags) (result '()))
59
    (cond
60
      ((equal? max-flag 1)
61
       (if (equal? flags 1)
62
           (cons (int->device-flags 1) result)
63
           result))
64
      ((< flags max-flag)
65
       (loop (/ max-flag 2) flags result))
66
      (else
67
        (loop (/ max-flag 2) (- flags max-flag)
68
              (cons
69
                (int->device-flags max-flag)
70
                result))))))
71
72
(define (get-links)
73
  (define request-num (random 65535))
74
  (define message
75
    (make-message
76
      RTM_GETLINK
77
      (logior NLM_F_REQUEST NLM_F_DUMP)
78
      request-num
79
      0
80
      (make-link-message AF_UNSPEC 0 0 0 0 '())))
81
82
  (let ((sock (connect-route)))
83
    (send-msg message sock)
84
    (let* ((answer (receive-and-decode-msg sock %default-route-decoder))
85
           (links (filter
86
                    (lambda (msg) (equal? (message-kind msg) RTM_NEWLINK))
87
                    answer))
88
           (links
89
             (map
90
               (lambda (msg)
91
                 (let* ((data (message-data msg))
92
                        (attrs (link-message-attrs data)))
93
                 (make-link
94
                   (get-attr attrs IFLA_IFNAME)
95
                   (link-message-index data)
96
                   (link-message-kind data)
97
                   (split-flags (link-message-flags data))
98
                   (get-attr attrs IFLA_MTU)
99
                   (get-attr attrs IFLA_QDISC)
100
                   (get-attr attrs IFLA_OPERSTATE)
101
                   (get-attr attrs IFLA_LINKMODE)
102
                   (get-attr attrs IFLA_GROUP)
103
                   (get-attr attrs IFLA_TXQLEN)
104
                   (get-attr attrs IFLA_ADDRESS)
105
                   (get-attr attrs IFLA_BROADCAST))))
106
               links)))
107
      (close-socket sock)
108
      links)))
109
110
(define* (link-show #:key (device #f) (group #f) (up #f) (master #f) (vrf #f)
111
                    (type #f))
112
  "Return a list whose elements represent the data about the links.  If a key
113
is given, the resulting list is limited to those elements that match the given
114
criteria."
115
  (define print-link
116
    (match-lambda
117
      (($ <link> name id type flags mtu qdisc state mode group qlen addr brd)
118
       (format #t "~a: ~a: <~a>" id name
119
               (string-join 
120
                 (map
121
                   (lambda (s)
122
                     ;; IFF_UP -> UP
123
                     (substring (symbol->string s) 4))
124
                   flags)
125
                 ","))
126
       (when mtu
127
         (format #t " mtu ~a" mtu))
128
       (when qdisc
129
         (format #t " qdisc ~a" qdisc))
130
       (when state
131
         (format #t " state ~a"
132
                 (substring (symbol->string (int->operstate state)) 8)))
133
       (when mode
134
         (format #t " mode ~a" (match mode (0 "DEFAULT") (1 "DORMANT"))))
135
       (when group
136
         (format #t " group ~a" (match group (0 "DEFAULT"))))
137
       (when qlen
138
         (format #t " qlen ~a" qlen))
139
       (newline)
140
       (cond
141
         ((equal? type ARPHRD_ETHER)
142
          (format #t "    link/ether ~a brd ~a~%" addr brd))
143
         ((equal? type ARPHRD_LOOPBACK)
144
          (format #t "    link/loopback ~a brd ~a~%" addr brd))))))
145
146
  (for-each
147
    (lambda (link)
148
      (match link
149
        (($ <link> lname lid ltype lflags lmtu lqdisc lstate lmode lgroup lqlen laddr lbrd)
150
         (when (and (or (not device) (equal? device lname))
151
                    (or (not group) (equal? group lgroup))
152
                    (or (not up) (member 'IFF_UP lflags))
153
                    ;(or (not master) ())
154
                    ;(or (not vrf) ())
155
                    (or (not type) (equal? type ltype)))
156
           (print-link link)))))
157
    (get-links)))
158
159
(define (link-name->index device)
160
  (let loop ((links (get-links)))
161
    (match links
162
      (() (throw 'no-such-device device))
163
      ((link links ...)
164
       (if (equal? (link-name link) device)
165
           (link-id link)
166
           (loop links))))))
167
168
(define (answer-ok? answer)
169
  (cond
170
    ((equal? (message-kind answer) NLMSG_DONE)
171
     #t)
172
    ((equal? (message-kind answer) NLMSG_ERROR)
173
     (let ((data (message-data answer)))
174
       (if (nl-data-data data)
175
           (let ((err (error-message-err data)))
176
             (if (equal? err 0)
177
                 #t
178
                 (begin
179
                   (format #t "RTNETLINK answers: ~a~%" (strerror (- err)))
180
                   #f)))
181
           #f)))))
182
183
(define* (link-set device #:key (up #f) (down #f) (type #f)
184
                   (arp-on #f) (arp-off #f)
185
                   (dynamic-on #f) (dynamic-off #f)
186
                   (multicast-on #f) (multicast-off #f)
187
                   (allmulticast-on #f) (allmulticast-off #f)
188
                   (promisc-on #f) (promisc-off #f)
189
                   (trailers-on #f) (trailers-off #f)
190
                   (carrier-on #f) (carrier-off #f)
191
                   (txqueuelen #f) (name #f) (address #f)
192
                   (broadcast #f) (mtu #f) (netns #f))
193
  (define request-num (random 65535))
194
  (define id (if (number? device) device (link-name->index device)))
195
  (define netnsfd (cond
196
                    ((string? netns)
197
                     (open (string-append "/var/run/netns/" netns) O_RDONLY))
198
                    ((number? netns)
199
                     (open (string-append "/var/run/netns/" (number->string netns))
200
                           O_RDONLY))
201
                    (else
202
                      #f)))
203
  (define message
204
    (make-message
205
      RTM_NEWLINK
206
      (logior NLM_F_REQUEST NLM_F_ACK)
207
      request-num
208
      0
209
      (make-link-message
210
        AF_UNSPEC
211
        (or type 0)
212
        id
213
        (+ (if up IFF_UP 0)
214
           (if arp-off IFF_NOARP 0)
215
           (if dynamic-on IFF_DYNAMIC 0)
216
           (if multicast-on IFF_MULTICAST 0)
217
           (if allmulticast-on IFF_ALLMULTI 0)
218
           (if promisc-on IFF_PROMISC 0)
219
           (if trailers-off IFF_NOTRAILERS 0))
220
        (+ (if (or up down) IFF_UP 0)
221
           (if (or arp-on arp-off) IFF_NOARP 0)
222
           (if (or dynamic-on dynamic-off) IFF_DYNAMIC 0)
223
           (if (or multicast-on multicast-off) IFF_MULTICAST 0)
224
           (if (or allmulticast-on allmulticast-off) IFF_ALLMULTI 0)
225
           (if (or promisc-on promisc-off) IFF_PROMISC 0)
226
           (if (or trailers-on trailers-off) IFF_NOTRAILERS 0))
227
        `(,@(if (or carrier-on carrier-off)
228
                (list
229
                  (make-route-attr IFLA_CARRIER
230
                    (make-u32-route-attr (if carrier-on 1 0))))
231
                '())
232
          ,@(if txqueuelen
233
                (list
234
                  (make-route-attr IFLA_TXQLEN
235
                    (make-u32-route-attr txqueuelen)))
236
                '())
237
          ,@(if name
238
                (list
239
                  (make-route-attr IFLA_TXQLEN
240
                    (make-u32-route-attr txqueuelen)))
241
                '())
242
          ,@(if address
243
                (list
244
                  (make-route-attr IFLA_ADDRESS
245
                    (make-ethernet-route-attr address)))
246
                '())
247
          ,@(if broadcast
248
                (list
249
                  (make-route-attr IFLA_BROADCAST
250
                    (make-ethernet-route-attr broadcast)))
251
                '())
252
          ,@(if mtu
253
                (list
254
                  (make-route-attr IFLA_MTU
255
                    (make-u32-route-attr mtu)))
256
                '())
257
          ,@(if netns
258
                (list
259
                  (make-route-attr IFLA_NET_NS_FD
260
                    (make-u32-route-attr
261
                      (fileno netnsfd))))
262
                '())))))
263
  (let ((sock (connect-route)))
264
    (send-msg message sock)
265
    (let ((answer (receive-and-decode-msg sock %default-route-decoder)))
266
      (when netnsfd
267
        (close netnsfd))
268
      (close-socket sock)
269
      (answer-ok? (last answer)))))
270
271
(define* (link-add name type #:key (type-args '()))
272
  (define request-num (random 65535))
273
  (define type-data
274
    (match type
275
      ("vlan"
276
       `(,@(if (assoc-ref type-args 'id)
277
               (list (make-route-attr IFLA_VLAN_ID
278
                       (make-u16-route-attr (assoc-ref type-args 'id))))
279
               '())))
280
      ("veth"
281
       `(,@(if (assoc-ref type-args 'peer)
282
               (list (make-route-attr VETH_INFO_PEER
283
                       (make-link-message
284
                         AF_UNSPEC 0 0 0 0
285
                         (list
286
                           (make-route-attr IFLA_IFNAME
287
                             (make-string-route-attr
288
                               (assoc-ref type-args 'peer)))))))
289
               '())))
290
      ;; TODO: unsupported for now
291
      (_ '())))
292
  (define message
293
    (make-message
294
      RTM_NEWLINK
295
      (logior NLM_F_REQUEST NLM_F_ACK NLM_F_EXCL NLM_F_CREATE)
296
      request-num
297
      0
298
      (make-link-message
299
        AF_UNSPEC
300
        0
301
        0
302
        0
303
        0
304
        (list
305
          (make-route-attr IFLA_IFNAME
306
            (make-string-route-attr name))
307
          (make-route-attr IFLA_LINKINFO
308
            (make-nested-route-attr
309
              (list
310
                (make-route-attr IFLA_INFO_KIND
311
                  (make-string-route-attr type))
312
                (make-route-attr IFLA_INFO_DATA
313
                  (make-nested-route-attr type-data)))))))))
314
  (let ((sock (connect-route)))
315
    (send-msg message sock)
316
    (let ((answer (receive-and-decode-msg sock %default-route-decoder)))
317
      (close-socket sock)
318
      (answer-ok? (last answer)))))
319
320
(define* (link-del device)
321
  (define request-num (random 65535))
322
323
  (define message
324
    (make-message
325
      RTM_DELLINK
326
      (logior NLM_F_REQUEST NLM_F_ACK)
327
      request-num
328
      0
329
      (make-link-message
330
        AF_UNSPEC
331
        0
332
        (cond
333
          ((number? device) device)
334
          ((string? device) (link-name->index device)))
335
        0
336
        0
337
        '())))
338
339
340
  (let ((sock (connect-route)))
341
    (send-msg message sock)
342
    (let ((answer (receive-and-decode-msg sock %default-route-decoder)))
343
      (close-socket sock)
344
      (answer-ok? (last answer)))))
345