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