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