guile-netlink/ip/route.scm

route.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 route)
19
  #:use-module (ice-9 match)
20
  #:use-module (ip link)
21
  #:use-module (ip utils)
22
  #:use-module (netlink route route)
23
  #:use-module (netlink route attrs)
24
  #:use-module (netlink connection)
25
  #:use-module (netlink constant)
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 (route-add
32
            route-del
33
            route-show))
34
35
(define-record-type <route>
36
  (make-route family table dest src gateway proto scope priority device)
37
  route?
38
  (family   route-family)
39
  (table    route-table)
40
  (dest     route-dest)
41
  (src      route-src)
42
  (gateway  route-gateway)
43
  (proto    route-proto)
44
  (scope    route-scope)
45
  (priority route-priority)
46
  (device   route-device))
47
48
(define* (route-del dest
49
                    #:key (ipv6? #f) (device #f) (table RT_TABLE_MAIN)
50
                          (protocol #f) (scope RT_SCOPE_NOWHERE) (type #f)
51
                          (priority #f) (src #f) (via #f))
52
  (define request-num (random 65535))
53
54
  (define index
55
    (cond
56
      ((number? device) device)
57
      ((string? device) (link-name->index device))
58
      (else #f)))
59
60
  (define message
61
    (make-message
62
      RTM_DELROUTE
63
      (logior NLM_F_REQUEST NLM_F_ACK)
64
      request-num
65
      0
66
      (make-route-message
67
        (if ipv6? AF_INET6 AF_INET)
68
        (if (equal? dest "default") 0 (cidr->prefix dest))
69
        (if src (cidr->prefix src) 0)
70
        0
71
        table
72
        (or protocol 0)
73
        scope
74
        (or type 0)
75
        0
76
        `(,@(if (equal? dest "default")
77
                '()
78
                (list (make-route-attr RTA_DST
79
                        ((if ipv6?
80
                             make-ipv6-route-attr
81
                             make-ipv4-route-attr)
82
                         (cidr->addr dest)))))
83
          ,@(if index
84
                (list (make-route-attr RTA_OIF
85
                        (make-u32-route-attr index)))
86
                '())
87
          ,@(if src
88
                (list (make-route-attr RTA_PREFSRC
89
                        ((if ipv6?
90
                             make-ipv6-route-attr
91
                             make-ipv4-route-attr)
92
                         (cidr->addr src))))
93
                '())
94
          ,@(if via
95
                (list (make-route-attr RTA_GATEWAY
96
                        ((if ipv6?
97
                             make-ipv6-route-attr
98
                             make-ipv4-route-attr)
99
                         via)))
100
                '())
101
          ,@(if priority
102
                (list (make-route-attr RTA_PRIORITY
103
                        (make-u32-route-attr priority)))
104
                '())))))
105
106
  (let ((sock (connect-route)))
107
    (send-msg message sock)
108
    (let ((answer (receive-and-decode-msg sock %default-route-decoder)))
109
      (close-port sock)
110
      (answer-ok? (last answer)))))
111
112
(define* (route-add dest
113
                    #:key (ipv6? #f) (device #f) (table RT_TABLE_MAIN)
114
                          (protocol RTPROT_BOOT) (scope RT_SCOPE_UNIVERSE)
115
                          (type RTN_UNICAST) (priority #f) (src #f) (via #f)
116
                          (onlink? #f))
117
  (define request-num (random 65535))
118
119
  (define index
120
    (cond
121
      ((number? device) device)
122
      ((string? device) (link-name->index device))
123
      (else #f)))
124
125
  (define message
126
    (make-message
127
      RTM_NEWROUTE
128
      (logior NLM_F_REQUEST NLM_F_ACK NLM_F_EXCL NLM_F_CREATE)
129
      request-num
130
      0
131
      (make-route-message
132
        (if ipv6? AF_INET6 AF_INET)
133
        (if (equal? dest "default") 0 (cidr->prefix dest))
134
        (if src (cidr->prefix src) 0)
135
        0
136
        table
137
        (or protocol 0)
138
        scope
139
        type
140
        (if onlink? RTNH_F_ONLINK 0)
141
        `(,@(if (equal? dest "default")
142
                '()
143
                (list (make-route-attr RTA_DST
144
                        ((if ipv6?
145
                             make-ipv6-route-attr
146
                             make-ipv4-route-attr)
147
                         (cidr->addr dest)))))
148
          ,@(if index
149
                (list (make-route-attr RTA_OIF
150
                        (make-u32-route-attr index)))
151
                '())
152
          ,@(if src
153
                (list (make-route-attr RTA_PREFSRC
154
                        ((if ipv6?
155
                             make-ipv6-route-attr
156
                             make-ipv4-route-attr)
157
                         (cidr->addr src))))
158
                '())
159
          ,@(if via
160
                (list (make-route-attr RTA_GATEWAY
161
                        ((if ipv6?
162
                             make-ipv6-route-attr
163
                             make-ipv4-route-attr)
164
                         via)))
165
                '())
166
          ,@(if priority
167
                (list (make-route-attr RTA_PRIORITY
168
                        (make-u32-route-attr priority)))
169
                '())))))
170
171
  (let ((sock (connect-route)))
172
    (send-msg message sock)
173
    (let ((answer (receive-and-decode-msg sock %default-route-decoder)))
174
      (close-port sock)
175
      (answer-ok? (last answer)))))
176
177
(define (link-ref links id)
178
  (let loop ((links links))
179
    (match links
180
      (() #f)
181
      ((link links ...)
182
       (if (equal? (link-id link) id)
183
           link
184
           (loop links))))))
185
186
(define (get-routes links)
187
  (define request-num (random 65535))
188
  (define message
189
    (make-message
190
      RTM_GETROUTE
191
      (logior NLM_F_REQUEST NLM_F_DUMP)
192
      request-num
193
      0
194
      (make-route-message AF_UNSPEC 0 0 0 0 0 0 0 0 '())))
195
  (let ((sock (connect-route)))
196
    (send-msg message sock)
197
    (let* ((answer (receive-and-decode-msg sock %default-route-decoder))
198
           (routes (filter
199
                     (lambda (msg) (equal? (message-kind msg) RTM_NEWROUTE))
200
                     answer))
201
           (routes (map
202
                     (lambda (msg)
203
                       (let* ((data (message-data msg))
204
                              (attrs (route-message-attrs data)))
205
                         (make-route
206
                           (route-message-family data)
207
                           (or (get-attr attrs RTA_TABLE)
208
                               (route-message-table data))
209
                           (let ((len (route-message-dest-len data))
210
                                 (dest (get-attr attrs RTA_DST)))
211
                             (if (or (equal? len 0) (not dest))
212
                                 #f
213
                                 (string-append dest "/" (number->string len))))
214
                           (let ((len (route-message-src-len data))
215
                                 (src (get-attr attrs RTA_PREFSRC)))
216
                             (if (or (equal? len 0) (not src))
217
                                 #f
218
                                 (string-append src "/" (number->string len))))
219
                           (get-attr attrs RTA_GATEWAY)
220
                           (route-message-protocol data)
221
                           (route-message-scope data)
222
                           (get-attr attrs RTA_PRIORITY)
223
                           (link-ref links (get-attr attrs RTA_OIF)))))
224
                     routes)))
225
      (close-port sock)
226
      routes)))
227
228
(define print-route
229
  (match-lambda
230
    (($ <route> family table dest src gateway proto scope priority device)
231
     (format #t "    ~a"
232
             (or dest "default"))
233
     (when gateway
234
       (format #t " via ~a" gateway))
235
     (when device
236
       (format #t " dev ~a" (link-name device)))
237
     (when (and proto (> proto 0))
238
       (format #t " proto ~a"
239
               (string-downcase
240
                 (substring (symbol->string (int->rtm-protocol proto)) 7))))
241
     (when (and scope (> scope 0))
242
       (format #t " scope ~a"
243
               (string-downcase
244
                 (substring (symbol->string (int->rtm-scope scope)) 9))))
245
     (when src
246
       (format #t " src ~a" src))
247
     (when priority
248
       (format #t " metric ~a" priority))
249
     (format #t "~%"))))
250
251
252
(define* (route-show #:key (table RT_TABLE_MAIN) (family AF_UNSPEC))
253
  (define links (get-links))
254
  (define routes (get-routes links))
255
256
  (for-each
257
    (lambda (route)
258
      (when (and (equal? (route-table route) table)
259
                 (or (equal? family AF_UNSPEC)
260
                     (equal? (route-family route) family)))
261
        (print-route route)))
262
    routes))
263