Add route high-level API
Makefile.am
14 | 14 | netlink/route/route.scm \ | |
15 | 15 | ip/addr.scm \ | |
16 | 16 | ip/link.scm \ | |
17 | + | ip/route.scm \ | |
17 | 18 | ip/utils.scm | |
18 | 19 | ||
19 | 20 | info_TEXINFOS= doc/guile-netlink.texi |
doc/guile-netlink.texi
47 | 47 | IP Library | |
48 | 48 | ||
49 | 49 | * Link:: Actions on network links. | |
50 | + | * Addr:: Actions on network addresses. | |
51 | + | * Route:: Actions on network routes. | |
50 | 52 | ||
51 | 53 | @end detailmenu | |
52 | 54 | @end menu | |
… | |||
614 | 616 | (addr-add "enp1s0" "192.0.2.15/24") | |
615 | 617 | @end example | |
616 | 618 | ||
617 | - | If you wish to remove an IPv6 address instead, set @code{#:ipv6} to @code{#t}, | |
619 | + | If you wish to add an IPv6 address instead, set @code{#:ipv6} to @code{#t}, | |
618 | 620 | as in the following example. | |
619 | 621 | ||
620 | 622 | @example | |
… | |||
653 | 655 | to addresses of that device. | |
654 | 656 | @end deffn | |
655 | 657 | ||
658 | + | @node Route | |
659 | + | @section Route | |
660 | + | ||
661 | + | The @code{(ip route)} module introduces procedures to access and modify the | |
662 | + | network routes on your machine. They are equivalent to the @command{ip route} | |
663 | + | family of commands, from @code{iproute2}. | |
664 | + | ||
665 | + | @deffn {Scheme Procedure} route-add @var{dest} [@var{#:ipv6?} #f] @ | |
666 | + | [@var{#:device} #f] [@var{#:table} RT_TABLE_MAIN] [@var{#:protocol} #f] @ | |
667 | + | [@var{#:scope} RT_SCOPE_LINK] [@var{#:type} RTN_UNICAST] @ | |
668 | + | [@var{#:priority} #f] [@var{#:src} #f] [@var{#:via} #f] | |
669 | + | Add the route described by the argmuents. @var{dest} is the destination network, | |
670 | + | in cidr notation (@code{addr/prefix}) or the string @code{"default"}. | |
671 | + | ||
672 | + | @var{#:device} is the name or index of a network link. @var{#:table} is the | |
673 | + | index of a routing table, one of @code{RT_TABLE_COMPAT}, @code{RT_TABLE_DEFAULT}, | |
674 | + | @code{RT_TABLE_MAIN} or @code{RT_TABLE_LOCAL}, as defined in | |
675 | + | @code{(netlink constant)}. | |
676 | + | ||
677 | + | If it is set, @var{#:protocol} must be the routing protocol, @code{RTPROT_*}, | |
678 | + | as defined in @code{(netlink constant)}. | |
679 | + | ||
680 | + | @var{#:scope} must be the scope of the route, one of @code{RT_SCOPE_*}, as | |
681 | + | defined in @code{(netlink constant)}. | |
682 | + | ||
683 | + | @var{#:type} must be the type of route, one of @code{RTN_*}, as defined in | |
684 | + | @code{(netlink constant)}. | |
685 | + | ||
686 | + | If set, @var{#:priority} is a number specifying the priority of the rule | |
687 | + | when the kernel is looking for a matching rule. This is also known as the | |
688 | + | metric of the route. | |
689 | + | ||
690 | + | If set, @var{#:src} is the source address in cidr notation, or as a single | |
691 | + | address. | |
692 | + | ||
693 | + | If set, @var{#:via} is the gateway address. This is not in cidr notation, as | |
694 | + | the gateway is a single address, not a network. | |
695 | + | ||
696 | + | @example | |
697 | + | (route-add "default" #:device "enp1s0" #:via "192.0.2.1") | |
698 | + | (route-add "192.0.2.0/24" #:device "enp1s0" #:src "192.0.2.15") | |
699 | + | @end example | |
700 | + | ||
701 | + | If you wish to add an IPv6 route instead, set @code{#:ipv6} to @code{#t}, | |
702 | + | as in the following example. | |
703 | + | ||
704 | + | @example | |
705 | + | (addr-add "2001:db8::/64" #:device "enp1s0" #:src "2001:db8::1a4c" #:ipv6? #t) | |
706 | + | @end example | |
707 | + | ||
708 | + | Note that using the wrong ip type with the wrong value for the @code{#:ipv6?} | |
709 | + | flag will result in a @code{Bad address} exception from inet-pton. | |
710 | + | @end deffn | |
711 | + | ||
712 | + | @deffn {Scheme Procedure} route-del @var{dest} [@var{#:ipv6?} #f] @ | |
713 | + | [@var{#:device} #f] [@var{#:table} RT_TABLE_MAIN] [@var{#:protocol} #f] @ | |
714 | + | [@var{#:scope} #f] [@var{#:type} #f] [@var{#:priority} #f] @ | |
715 | + | [@var{#:src} #f] [@var{#:via} #f] | |
716 | + | Delete the route given in arguments. The arguments follow the same structure | |
717 | + | as @code{route-add}. By specifying more arguments, you can narrow down the | |
718 | + | search for the rule to delete further. Each call will only remove one route, | |
719 | + | so being more precise ensures you target the rule you wish to delete. It | |
720 | + | is not clear which route is deleted if multiple routes match your query. | |
721 | + | @end deffn | |
722 | + | ||
723 | + | @deffn {Scheme Procedure} route-show [@var{#:table} RT_TABLE_MAIN] @ | |
724 | + | [@var{#:family} AF_UNSPEC] | |
725 | + | Print the list of routes on standard output. Note that, contrary to | |
726 | + | @command{ip route show}, we show both IPv4 and IPv6 routes. To narrow down the | |
727 | + | number of routes displayed, you can specify the family as in this example. | |
728 | + | ||
729 | + | @example | |
730 | + | (route-show #:family AF_INET6) | |
731 | + | @end example | |
732 | + | @end deffn | |
733 | + | ||
656 | 734 | @bye |
ip/route.scm unknown status 1
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 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-socket sock) | |
110 | + | (answer-ok? (last answer))))) | |
111 | + | ||
112 | + | (define* (route-add dest | |
113 | + | #:key (ipv6? #f) (device #f) (table RT_TABLE_MAIN) | |
114 | + | (protocol #f) (scope RT_SCOPE_LINK) | |
115 | + | (type RTN_UNICAST) (priority #f) (src #f) (via #f)) | |
116 | + | (define request-num (random 65535)) | |
117 | + | ||
118 | + | (define index | |
119 | + | (cond | |
120 | + | ((number? device) device) | |
121 | + | ((string? device) (link-name->index device)) | |
122 | + | (else #f))) | |
123 | + | ||
124 | + | (define message | |
125 | + | (make-message | |
126 | + | RTM_NEWROUTE | |
127 | + | (logior NLM_F_REQUEST NLM_F_ACK NLM_F_EXCL NLM_F_CREATE) | |
128 | + | request-num | |
129 | + | 0 | |
130 | + | (make-route-message | |
131 | + | (if ipv6? AF_INET6 AF_INET) | |
132 | + | (if (equal? dest "default") 0 (cidr->prefix dest)) | |
133 | + | (if src (cidr->prefix src) 0) | |
134 | + | 0 | |
135 | + | table | |
136 | + | (or protocol 0) | |
137 | + | scope | |
138 | + | type | |
139 | + | 0 | |
140 | + | `(,@(if (equal? dest "default") | |
141 | + | '() | |
142 | + | (list (make-route-attr RTA_DST | |
143 | + | ((if ipv6? | |
144 | + | make-ipv6-route-attr | |
145 | + | make-ipv4-route-attr) | |
146 | + | (cidr->addr dest))))) | |
147 | + | ,@(if index | |
148 | + | (list (make-route-attr RTA_OIF | |
149 | + | (make-u32-route-attr index))) | |
150 | + | '()) | |
151 | + | ,@(if src | |
152 | + | (list (make-route-attr RTA_PREFSRC | |
153 | + | ((if ipv6? | |
154 | + | make-ipv6-route-attr | |
155 | + | make-ipv4-route-attr) | |
156 | + | (cidr->addr src)))) | |
157 | + | '()) | |
158 | + | ,@(if via | |
159 | + | (list (make-route-attr RTA_GATEWAY | |
160 | + | ((if ipv6? | |
161 | + | make-ipv6-route-attr | |
162 | + | make-ipv4-route-attr) | |
163 | + | via))) | |
164 | + | '()) | |
165 | + | ,@(if priority | |
166 | + | (list (make-route-attr RTA_PRIORITY | |
167 | + | (make-u32-route-attr priority))) | |
168 | + | '()))))) | |
169 | + | ||
170 | + | (let ((sock (connect-route))) | |
171 | + | (send-msg message sock) | |
172 | + | (let ((answer (receive-and-decode-msg sock %default-route-decoder))) | |
173 | + | (close-socket sock) | |
174 | + | (answer-ok? (last answer))))) | |
175 | + | ||
176 | + | (define (link-ref links id) | |
177 | + | (let loop ((links links)) | |
178 | + | (match links | |
179 | + | (() #f) | |
180 | + | ((link links ...) | |
181 | + | (if (equal? (link-id link) id) | |
182 | + | link | |
183 | + | (loop links)))))) | |
184 | + | ||
185 | + | (define (get-routes links) | |
186 | + | (define request-num (random 65535)) | |
187 | + | (define message | |
188 | + | (make-message | |
189 | + | RTM_GETROUTE | |
190 | + | (logior NLM_F_REQUEST NLM_F_DUMP) | |
191 | + | request-num | |
192 | + | 0 | |
193 | + | (make-route-message AF_UNSPEC 0 0 0 0 0 0 0 0 '()))) | |
194 | + | (let ((sock (connect-route))) | |
195 | + | (send-msg message sock) | |
196 | + | (let* ((answer (receive-and-decode-msg sock %default-route-decoder)) | |
197 | + | (routes (filter | |
198 | + | (lambda (msg) (equal? (message-kind msg) RTM_NEWROUTE)) | |
199 | + | answer)) | |
200 | + | (routes (map | |
201 | + | (lambda (msg) | |
202 | + | (let* ((data (message-data msg)) | |
203 | + | (attrs (route-message-attrs data))) | |
204 | + | (make-route | |
205 | + | (route-message-family data) | |
206 | + | (or (get-attr attrs RTA_TABLE) | |
207 | + | (route-message-table data)) | |
208 | + | (let ((len (route-message-dest-len data)) | |
209 | + | (dest (get-attr attrs RTA_DST))) | |
210 | + | (if (or (equal? len 0) (not dest)) | |
211 | + | #f | |
212 | + | (string-append dest "/" (number->string len)))) | |
213 | + | (let ((len (route-message-src-len data)) | |
214 | + | (src (get-attr attrs RTA_PREFSRC))) | |
215 | + | (if (or (equal? len 0) (not src)) | |
216 | + | #f | |
217 | + | (string-append src "/" (number->string len)))) | |
218 | + | (get-attr attrs RTA_GATEWAY) | |
219 | + | (route-message-protocol data) | |
220 | + | (route-message-scope data) | |
221 | + | (get-attr attrs RTA_PRIORITY) | |
222 | + | (link-ref links (get-attr attrs RTA_OIF))))) | |
223 | + | routes))) | |
224 | + | (close-socket sock) | |
225 | + | routes))) | |
226 | + | ||
227 | + | (define print-route | |
228 | + | (match-lambda | |
229 | + | (($ <route> family table dest src gateway proto scope priority device) | |
230 | + | (format #t " ~a" | |
231 | + | (or dest "default")) | |
232 | + | (when gateway | |
233 | + | (format #t " via ~a" gateway)) | |
234 | + | (when device | |
235 | + | (format #t " dev ~a" (link-name device))) | |
236 | + | (when (and proto (> proto 0)) | |
237 | + | (format #t " proto ~a" | |
238 | + | (string-downcase | |
239 | + | (substring (symbol->string (int->rtm-protocol proto)) 7)))) | |
240 | + | (when (and scope (> scope 0)) | |
241 | + | (format #t " scope ~a" | |
242 | + | (string-downcase | |
243 | + | (substring (symbol->string (int->rtm-scope scope)) 9)))) | |
244 | + | (when src | |
245 | + | (format #t " src ~a" src)) | |
246 | + | (when priority | |
247 | + | (format #t " metric ~a" priority)) | |
248 | + | (format #t "~%")))) | |
249 | + | ||
250 | + | ||
251 | + | (define* (route-show #:key (table RT_TABLE_MAIN) (family AF_UNSPEC)) | |
252 | + | (define links (get-links)) | |
253 | + | (define routes (get-routes links)) | |
254 | + | ||
255 | + | (for-each | |
256 | + | (lambda (route) | |
257 | + | (when (and (equal? (route-table route) table) | |
258 | + | (or (equal? family AF_UNSPEC) | |
259 | + | (equal? (route-family route) family))) | |
260 | + | (print-route route))) | |
261 | + | routes)) |