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