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