]> git.proxmox.com Git - mirror_iproute2.git/blame - ip/iprule.c
ip rule: merge ip rule flush and list, save together
[mirror_iproute2.git] / ip / iprule.c
CommitLineData
aba5acdf
SH
1/*
2 * iprule.c "ip rule".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
aba5acdf
SH
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <netinet/ip.h>
21#include <arpa/inet.h>
22#include <string.h>
3123a0cc 23#include <linux/fib_rules.h>
2f4e171f 24#include <errno.h>
aba5acdf
SH
25
26#include "rt_names.h"
27#include "utils.h"
34e95647 28#include "ip_common.h"
aba5acdf 29
cb294a1d
HL
30enum list_action {
31 IPRULE_LIST,
32 IPRULE_FLUSH,
33 IPRULE_SAVE,
34};
35
351efcde
SH
36extern struct rtnl_handle rth;
37
aba5acdf
SH
38static void usage(void) __attribute__((noreturn));
39
40static void usage(void)
41{
67eedcd9
PS
42 fprintf(stderr, "Usage: ip rule { add | del } SELECTOR ACTION\n");
43 fprintf(stderr, " ip rule { flush | save | restore }\n");
44 fprintf(stderr, " ip rule [ list ]\n");
be7f286e 45 fprintf(stderr, "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n");
8c92e122 46 fprintf(stderr, " [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n");
526afe40 47 fprintf(stderr, "ACTION := [ table TABLE_ID ]\n");
20f2af78 48 fprintf(stderr, " [ nat ADDRESS ]\n");
aba5acdf 49 fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
6b469cae 50 fprintf(stderr, " [ goto NUMBER ]\n");
b1d0525f
ST
51 fprintf(stderr, " SUPPRESSOR\n");
52 fprintf(stderr, "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n");
53 fprintf(stderr, " [ suppress_ifgroup DEVGROUP ]\n");
aba5acdf
SH
54 fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
55 exit(-1);
56}
57
98bde989 58int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
aba5acdf 59{
56f5daac 60 FILE *fp = (FILE *)arg;
aba5acdf
SH
61 struct rtmsg *r = NLMSG_DATA(n);
62 int len = n->nlmsg_len;
63 int host_len = -1;
34e95647 64 __u32 table;
56f5daac 65 struct rtattr *tb[FRA_MAX+1];
d831cc7c 66
aba5acdf
SH
67 SPRINT_BUF(b1);
68
98bde989 69 if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE)
aba5acdf
SH
70 return 0;
71
72 len -= NLMSG_LENGTH(sizeof(*r));
73 if (len < 0)
74 return -1;
75
ad1a12db 76 parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len);
aba5acdf 77
f3a2ddc1 78 host_len = af_bit_len(r->rtm_family);
aba5acdf 79
98bde989
TG
80 if (n->nlmsg_type == RTM_DELRULE)
81 fprintf(fp, "Deleted ");
82
ad1a12db 83 if (tb[FRA_PRIORITY])
d831cc7c
SH
84 fprintf(fp, "%u:\t",
85 rta_getattr_u32(tb[FRA_PRIORITY]));
aba5acdf
SH
86 else
87 fprintf(fp, "0:\t");
88
3123a0cc
TG
89 if (r->rtm_flags & FIB_RULE_INVERT)
90 fprintf(fp, "not ");
91
ad1a12db 92 if (tb[FRA_SRC]) {
aba5acdf 93 if (r->rtm_src_len != host_len) {
7faf1588 94 fprintf(fp, "from %s/%u ",
d831cc7c
SH
95 rt_addr_n2a_rta(r->rtm_family, tb[FRA_SRC]),
96 r->rtm_src_len);
aba5acdf 97 } else {
d49f934c 98 fprintf(fp, "from %s ",
d831cc7c 99 format_host_rta(r->rtm_family, tb[FRA_SRC]));
aba5acdf
SH
100 }
101 } else if (r->rtm_src_len) {
102 fprintf(fp, "from 0/%d ", r->rtm_src_len);
103 } else {
104 fprintf(fp, "from all ");
105 }
106
ad1a12db 107 if (tb[FRA_DST]) {
aba5acdf 108 if (r->rtm_dst_len != host_len) {
7faf1588 109 fprintf(fp, "to %s/%u ",
d831cc7c
SH
110 rt_addr_n2a_rta(r->rtm_family, tb[FRA_DST]),
111 r->rtm_dst_len);
aba5acdf 112 } else {
d49f934c 113 fprintf(fp, "to %s ",
d831cc7c 114 format_host_rta(r->rtm_family, tb[FRA_DST]));
aba5acdf
SH
115 }
116 } else if (r->rtm_dst_len) {
117 fprintf(fp, "to 0/%d ", r->rtm_dst_len);
118 }
119
120 if (r->rtm_tos) {
121 SPRINT_BUF(b1);
d831cc7c
SH
122 fprintf(fp, "tos %s ",
123 rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
aba5acdf 124 }
ad1a12db 125
4806867a 126 if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) {
be7f286e
PM
127 __u32 mark = 0, mask = 0;
128
ad1a12db 129 if (tb[FRA_FWMARK])
ff24746c 130 mark = rta_getattr_u32(tb[FRA_FWMARK]);
be7f286e 131
ad1a12db 132 if (tb[FRA_FWMASK] &&
ff24746c 133 (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF)
be7f286e 134 fprintf(fp, "fwmark 0x%x/0x%x ", mark, mask);
81c61790 135 else
be7f286e 136 fprintf(fp, "fwmark 0x%x ", mark);
aba5acdf
SH
137 }
138
ad1a12db 139 if (tb[FRA_IFNAME]) {
ff24746c 140 fprintf(fp, "iif %s ", rta_getattr_str(tb[FRA_IFNAME]));
85eae222
PM
141 if (r->rtm_flags & FIB_RULE_IIF_DETACHED)
142 fprintf(fp, "[detached] ");
143 }
144
145 if (tb[FRA_OIFNAME]) {
ff24746c 146 fprintf(fp, "oif %s ", rta_getattr_str(tb[FRA_OIFNAME]));
85eae222 147 if (r->rtm_flags & FIB_RULE_OIF_DETACHED)
6b469cae 148 fprintf(fp, "[detached] ");
aba5acdf
SH
149 }
150
8c92e122
DA
151 if (tb[FRA_L3MDEV]) {
152 if (rta_getattr_u8(tb[FRA_L3MDEV]))
153 fprintf(fp, "lookup [l3mdev-table] ");
154 }
155
34e95647 156 table = rtm_get_table(r, tb);
b1d0525f 157 if (table) {
d831cc7c
SH
158 fprintf(fp, "lookup %s ",
159 rtnl_rttable_n2a(table, b1, sizeof(b1)));
aba5acdf 160
b1d0525f
ST
161 if (tb[FRA_SUPPRESS_PREFIXLEN]) {
162 int pl = rta_getattr_u32(tb[FRA_SUPPRESS_PREFIXLEN]);
56f5daac 163
d831cc7c 164 if (pl != -1)
b1d0525f 165 fprintf(fp, "suppress_prefixlength %d ", pl);
b1d0525f
ST
166 }
167 if (tb[FRA_SUPPRESS_IFGROUP]) {
168 int group = rta_getattr_u32(tb[FRA_SUPPRESS_IFGROUP]);
56f5daac 169
b1d0525f
ST
170 if (group != -1) {
171 SPRINT_BUF(b1);
d831cc7c
SH
172 fprintf(fp, "suppress_ifgroup %s ",
173 rtnl_group_n2a(group, b1, sizeof(b1)));
b1d0525f
ST
174 }
175 }
176 }
177
ad1a12db 178 if (tb[FRA_FLOW]) {
ff24746c 179 __u32 to = rta_getattr_u32(tb[FRA_FLOW]);
aba5acdf 180 __u32 from = to>>16;
56f5daac 181
aba5acdf
SH
182 to &= 0xFFFF;
183 if (from) {
184 fprintf(fp, "realms %s/",
185 rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
186 }
187 fprintf(fp, "%s ",
188 rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
189 }
190
191 if (r->rtm_type == RTN_NAT) {
192 if (tb[RTA_GATEWAY]) {
ae665a52 193 fprintf(fp, "map-to %s ",
d831cc7c
SH
194 format_host_rta(r->rtm_family,
195 tb[RTA_GATEWAY]));
aba5acdf
SH
196 } else
197 fprintf(fp, "masquerade");
6b469cae
TG
198 } else if (r->rtm_type == FR_ACT_GOTO) {
199 fprintf(fp, "goto ");
200 if (tb[FRA_GOTO])
ff24746c 201 fprintf(fp, "%u", rta_getattr_u32(tb[FRA_GOTO]));
6b469cae
TG
202 else
203 fprintf(fp, "none");
204 if (r->rtm_flags & FIB_RULE_UNRESOLVED)
205 fprintf(fp, " [unresolved]");
206 } else if (r->rtm_type == FR_ACT_NOP)
207 fprintf(fp, "nop");
208 else if (r->rtm_type != RTN_UNICAST)
d831cc7c
SH
209 fprintf(fp, "%s",
210 rtnl_rtntype_n2a(r->rtm_type,
211 b1, sizeof(b1)));
aba5acdf
SH
212
213 fprintf(fp, "\n");
214 fflush(fp);
215 return 0;
216}
217
2f4e171f
KT
218static __u32 rule_dump_magic = 0x71706986;
219
220static int save_rule_prep(void)
221{
222 int ret;
223
224 if (isatty(STDOUT_FILENO)) {
225 fprintf(stderr, "Not sending a binary stream to stdout\n");
226 return -1;
227 }
228
229 ret = write(STDOUT_FILENO, &rule_dump_magic, sizeof(rule_dump_magic));
230 if (ret != sizeof(rule_dump_magic)) {
231 fprintf(stderr, "Can't write magic to dump file\n");
232 return -1;
233 }
234
235 return 0;
236}
237
d831cc7c
SH
238static int save_rule(const struct sockaddr_nl *who,
239 struct nlmsghdr *n, void *arg)
aba5acdf 240{
2f4e171f
KT
241 int ret;
242
243 ret = write(STDOUT_FILENO, n, n->nlmsg_len);
244 if ((ret > 0) && (ret != n->nlmsg_len)) {
245 fprintf(stderr, "Short write while saving nlmsg\n");
246 ret = -EIO;
247 }
248
249 return ret == n->nlmsg_len ? 0 : ret;
250}
251
cb294a1d
HL
252static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n,
253 void *arg)
254{
255 struct rtnl_handle rth2;
256 struct rtmsg *r = NLMSG_DATA(n);
257 int len = n->nlmsg_len;
258 struct rtattr *tb[FRA_MAX+1];
259
260 len -= NLMSG_LENGTH(sizeof(*r));
261 if (len < 0)
262 return -1;
263
264 parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len);
265
266 if (tb[FRA_PRIORITY]) {
267 n->nlmsg_type = RTM_DELRULE;
268 n->nlmsg_flags = NLM_F_REQUEST;
269
270 if (rtnl_open(&rth2, 0) < 0)
271 return -1;
272
273 if (rtnl_talk(&rth2, n, NULL, 0) < 0)
274 return -2;
275
276 rtnl_close(&rth2);
277 }
278
279 return 0;
280}
281
282static int iprule_list_flush_or_save(int argc, char **argv, int action)
2f4e171f 283{
cb294a1d 284 rtnl_filter_t filter_fn;
aba5acdf
SH
285 int af = preferred_family;
286
287 if (af == AF_UNSPEC)
288 af = AF_INET;
289
290 if (argc > 0) {
cb294a1d
HL
291 fprintf(stderr,
292 "\"ip rule list/flush/save\" does not take any arguments\n");
aba5acdf
SH
293 return -1;
294 }
295
cb294a1d
HL
296 switch (action) {
297 case IPRULE_SAVE:
2f4e171f
KT
298 if (save_rule_prep())
299 return -1;
cb294a1d
HL
300 filter_fn = save_rule;
301 break;
302 case IPRULE_FLUSH:
303 filter_fn = flush_rule;
304 break;
305 default:
306 filter_fn = print_rule;
2f4e171f
KT
307 }
308
aba5acdf
SH
309 if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
310 perror("Cannot send dump request");
311 return 1;
312 }
313
cb294a1d 314 if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
aba5acdf
SH
315 fprintf(stderr, "Dump terminated\n");
316 return 1;
317 }
318
319 return 0;
320}
321
2f4e171f
KT
322static int rule_dump_check_magic(void)
323{
324 int ret;
325 __u32 magic = 0;
326
327 if (isatty(STDIN_FILENO)) {
328 fprintf(stderr, "Can't restore rule dump from a terminal\n");
329 return -1;
330 }
331
332 ret = fread(&magic, sizeof(magic), 1, stdin);
333 if (magic != rule_dump_magic) {
d831cc7c
SH
334 fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n",
335 ret, magic);
2f4e171f
KT
336 return -1;
337 }
338
339 return 0;
340}
341
342static int restore_handler(const struct sockaddr_nl *nl,
343 struct rtnl_ctrl_data *ctrl,
344 struct nlmsghdr *n, void *arg)
345{
346 int ret;
347
348 n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
349
350 ll_init_map(&rth);
351
352 ret = rtnl_talk(&rth, n, n, sizeof(*n));
353 if ((ret < 0) && (errno == EEXIST))
354 ret = 0;
355
356 return ret;
357}
358
359
360static int iprule_restore(void)
361{
362 if (rule_dump_check_magic())
363 exit(-1);
364
365 exit(rtnl_from_file(stdin, &restore_handler, NULL));
366}
aba5acdf 367
50772dc5 368static int iprule_modify(int cmd, int argc, char **argv)
aba5acdf 369{
8c92e122 370 int l3mdev_rule = 0;
aba5acdf 371 int table_ok = 0;
8c92e122 372 __u32 tid = 0;
aba5acdf 373 struct {
4806867a
SH
374 struct nlmsghdr n;
375 struct rtmsg r;
56f5daac 376 char buf[1024];
d17b136f
PS
377 } req = {
378 .n.nlmsg_type = cmd,
379 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
380 .n.nlmsg_flags = NLM_F_REQUEST,
381 .r.rtm_family = preferred_family,
382 .r.rtm_protocol = RTPROT_BOOT,
383 .r.rtm_scope = RT_SCOPE_UNIVERSE,
384 .r.rtm_type = RTN_UNSPEC,
385 };
aba5acdf
SH
386
387 if (cmd == RTM_NEWRULE) {
388 req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
389 req.r.rtm_type = RTN_UNICAST;
390 }
391
67a990b8
AJM
392 if (cmd == RTM_DELRULE && argc == 0) {
393 fprintf(stderr, "\"ip rule del\" requires arguments.\n");
394 return -1;
395 }
396
aba5acdf 397 while (argc > 0) {
3123a0cc
TG
398 if (strcmp(*argv, "not") == 0) {
399 req.r.rtm_flags |= FIB_RULE_INVERT;
400 } else if (strcmp(*argv, "from") == 0) {
aba5acdf 401 inet_prefix dst;
56f5daac 402
aba5acdf
SH
403 NEXT_ARG();
404 get_prefix(&dst, *argv, req.r.rtm_family);
405 req.r.rtm_src_len = dst.bitlen;
d831cc7c
SH
406 addattr_l(&req.n, sizeof(req), FRA_SRC,
407 &dst.data, dst.bytelen);
aba5acdf
SH
408 } else if (strcmp(*argv, "to") == 0) {
409 inet_prefix dst;
56f5daac 410
aba5acdf
SH
411 NEXT_ARG();
412 get_prefix(&dst, *argv, req.r.rtm_family);
413 req.r.rtm_dst_len = dst.bitlen;
d831cc7c
SH
414 addattr_l(&req.n, sizeof(req), FRA_DST,
415 &dst.data, dst.bytelen);
aba5acdf
SH
416 } else if (matches(*argv, "preference") == 0 ||
417 matches(*argv, "order") == 0 ||
418 matches(*argv, "priority") == 0) {
419 __u32 pref;
56f5daac 420
aba5acdf
SH
421 NEXT_ARG();
422 if (get_u32(&pref, *argv, 0))
423 invarg("preference value is invalid\n", *argv);
ad1a12db 424 addattr32(&req.n, sizeof(req), FRA_PRIORITY, pref);
dec01609
AH
425 } else if (strcmp(*argv, "tos") == 0 ||
426 matches(*argv, "dsfield") == 0) {
aba5acdf 427 __u32 tos;
56f5daac 428
aba5acdf
SH
429 NEXT_ARG();
430 if (rtnl_dsfield_a2n(&tos, *argv))
431 invarg("TOS value is invalid\n", *argv);
432 req.r.rtm_tos = tos;
433 } else if (strcmp(*argv, "fwmark") == 0) {
be7f286e
PM
434 char *slash;
435 __u32 fwmark, fwmask;
56f5daac 436
aba5acdf 437 NEXT_ARG();
d831cc7c
SH
438
439 slash = strchr(*argv, '/');
440 if (slash != NULL)
be7f286e 441 *slash = '\0';
4fb466f9 442 if (get_u32(&fwmark, *argv, 0))
aba5acdf 443 invarg("fwmark value is invalid\n", *argv);
ad1a12db 444 addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark);
be7f286e
PM
445 if (slash) {
446 if (get_u32(&fwmask, slash+1, 0))
d831cc7c
SH
447 invarg("fwmask value is invalid\n",
448 slash+1);
449 addattr32(&req.n, sizeof(req),
450 FRA_FWMASK, fwmask);
be7f286e 451 }
aba5acdf
SH
452 } else if (matches(*argv, "realms") == 0) {
453 __u32 realm;
56f5daac 454
aba5acdf 455 NEXT_ARG();
d583e88e 456 if (get_rt_realms_or_raw(&realm, *argv))
aba5acdf 457 invarg("invalid realms\n", *argv);
ad1a12db 458 addattr32(&req.n, sizeof(req), FRA_FLOW, realm);
aba5acdf
SH
459 } else if (matches(*argv, "table") == 0 ||
460 strcmp(*argv, "lookup") == 0) {
aba5acdf
SH
461 NEXT_ARG();
462 if (rtnl_rttable_a2n(&tid, *argv))
463 invarg("invalid table ID\n", *argv);
34e95647
PM
464 if (tid < 256)
465 req.r.rtm_table = tid;
466 else {
467 req.r.rtm_table = RT_TABLE_UNSPEC;
ad1a12db 468 addattr32(&req.n, sizeof(req), FRA_TABLE, tid);
34e95647 469 }
aba5acdf 470 table_ok = 1;
b1d0525f
ST
471 } else if (matches(*argv, "suppress_prefixlength") == 0 ||
472 strcmp(*argv, "sup_pl") == 0) {
473 int pl;
56f5daac 474
b1d0525f
ST
475 NEXT_ARG();
476 if (get_s32(&pl, *argv, 0) || pl < 0)
d831cc7c
SH
477 invarg("suppress_prefixlength value is invalid\n",
478 *argv);
479 addattr32(&req.n, sizeof(req),
480 FRA_SUPPRESS_PREFIXLEN, pl);
b1d0525f
ST
481 } else if (matches(*argv, "suppress_ifgroup") == 0 ||
482 strcmp(*argv, "sup_group") == 0) {
483 NEXT_ARG();
484 int group;
56f5daac 485
b1d0525f 486 if (rtnl_group_a2n(&group, *argv))
d831cc7c
SH
487 invarg("Invalid \"suppress_ifgroup\" value\n",
488 *argv);
489 addattr32(&req.n, sizeof(req),
490 FRA_SUPPRESS_IFGROUP, group);
aba5acdf
SH
491 } else if (strcmp(*argv, "dev") == 0 ||
492 strcmp(*argv, "iif") == 0) {
493 NEXT_ARG();
d831cc7c
SH
494 addattr_l(&req.n, sizeof(req), FRA_IFNAME,
495 *argv, strlen(*argv)+1);
85eae222
PM
496 } else if (strcmp(*argv, "oif") == 0) {
497 NEXT_ARG();
d831cc7c
SH
498 addattr_l(&req.n, sizeof(req), FRA_OIFNAME,
499 *argv, strlen(*argv)+1);
8c92e122
DA
500 } else if (strcmp(*argv, "l3mdev") == 0) {
501 addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1);
502 table_ok = 1;
503 l3mdev_rule = 1;
aba5acdf
SH
504 } else if (strcmp(*argv, "nat") == 0 ||
505 matches(*argv, "map-to") == 0) {
506 NEXT_ARG();
526afe40 507 fprintf(stderr, "Warning: route NAT is deprecated\n");
d831cc7c
SH
508 addattr32(&req.n, sizeof(req), RTA_GATEWAY,
509 get_addr32(*argv));
aba5acdf
SH
510 req.r.rtm_type = RTN_NAT;
511 } else {
512 int type;
513
d831cc7c 514 if (strcmp(*argv, "type") == 0)
aba5acdf 515 NEXT_ARG();
d831cc7c 516
aba5acdf
SH
517 if (matches(*argv, "help") == 0)
518 usage();
6b469cae
TG
519 else if (matches(*argv, "goto") == 0) {
520 __u32 target;
56f5daac 521
6b469cae
TG
522 type = FR_ACT_GOTO;
523 NEXT_ARG();
524 if (get_u32(&target, *argv, 0))
525 invarg("invalid target\n", *argv);
d831cc7c
SH
526 addattr32(&req.n, sizeof(req),
527 FRA_GOTO, target);
6b469cae
TG
528 } else if (matches(*argv, "nop") == 0)
529 type = FR_ACT_NOP;
530 else if (rtnl_rtntype_a2n(&type, *argv))
aba5acdf
SH
531 invarg("Failed to parse rule type", *argv);
532 req.r.rtm_type = type;
6b469cae 533 table_ok = 1;
aba5acdf
SH
534 }
535 argc--;
536 argv++;
537 }
538
8c92e122
DA
539 if (l3mdev_rule && tid != 0) {
540 fprintf(stderr,
541 "table can not be specified for l3mdev rules\n");
542 return -EINVAL;
543 }
544
aba5acdf
SH
545 if (req.r.rtm_family == AF_UNSPEC)
546 req.r.rtm_family = AF_INET;
547
548 if (!table_ok && cmd == RTM_NEWRULE)
549 req.r.rtm_table = RT_TABLE_MAIN;
550
c079e121 551 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
076ae708 552 return -2;
aba5acdf
SH
553
554 return 0;
555}
556
557int do_iprule(int argc, char **argv)
558{
559 if (argc < 1) {
cb294a1d 560 return iprule_list_flush_or_save(0, NULL, IPRULE_LIST);
aba5acdf
SH
561 } else if (matches(argv[0], "list") == 0 ||
562 matches(argv[0], "lst") == 0 ||
563 matches(argv[0], "show") == 0) {
cb294a1d 564 return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_LIST);
2f4e171f 565 } else if (matches(argv[0], "save") == 0) {
cb294a1d 566 return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_SAVE);
2f4e171f
KT
567 } else if (matches(argv[0], "restore") == 0) {
568 return iprule_restore();
aba5acdf
SH
569 } else if (matches(argv[0], "add") == 0) {
570 return iprule_modify(RTM_NEWRULE, argc-1, argv+1);
571 } else if (matches(argv[0], "delete") == 0) {
572 return iprule_modify(RTM_DELRULE, argc-1, argv+1);
50772dc5 573 } else if (matches(argv[0], "flush") == 0) {
cb294a1d 574 return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_FLUSH);
aba5acdf
SH
575 } else if (matches(argv[0], "help") == 0)
576 usage();
577
d831cc7c
SH
578 fprintf(stderr,
579 "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv);
aba5acdf
SH
580 exit(-1);
581}
582
b6c8e808
PM
583int do_multirule(int argc, char **argv)
584{
585 switch (preferred_family) {
586 case AF_UNSPEC:
587 case AF_INET:
588 preferred_family = RTNL_FAMILY_IPMR;
589 break;
590 case AF_INET6:
591 preferred_family = RTNL_FAMILY_IP6MR;
592 break;
0d1c9b57
BG
593 case RTNL_FAMILY_IPMR:
594 case RTNL_FAMILY_IP6MR:
595 break;
b6c8e808 596 default:
d831cc7c
SH
597 fprintf(stderr,
598 "Multicast rules are only supported for IPv4/IPv6, was: %i\n",
0d1c9b57 599 preferred_family);
b6c8e808
PM
600 exit(-1);
601 }
602
603 return do_iprule(argc, argv);
604}