guile-netlink/ip/link.scm

link.scm

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