From 2f4e171f7df22107b38fddcffa56c1ecb5e73359 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Tue, 20 Oct 2015 13:41:48 +0300 Subject: [PATCH 1/3] Add ip rule save/restore This patch adds save and restore commands to "ip rule" similar the same is made in commit f4ff11e3e298 for "ip route". The feature is useful in checkpoint/restore for container migration, also it may be helpful in some normal situations. Signed-off-by: Kirill Tkhai --- doc/ip-cref.tex | 36 ++++++++++++++++ ip/iprule.c | 103 ++++++++++++++++++++++++++++++++++++++++++--- man/man8/ip-rule.8 | 26 +++++++++++- 3 files changed, 158 insertions(+), 7 deletions(-) diff --git a/doc/ip-cref.tex b/doc/ip-cref.tex index ea147950..67094c95 100644 --- a/doc/ip-cref.tex +++ b/doc/ip-cref.tex @@ -2246,6 +2246,42 @@ Besides that, the host 193.233.7.83 is translated into another prefix to look like 192.203.80.144 when talking to the outer world. +\subsection{{\tt ip rule save} -- save rules tables} +\label{IP-RULE-SAVE} + +\paragraph{Description:} this command saves the contents of the rules +tables or the rule(s) selected by some criteria to standard output. + +\paragraph{Arguments:} \verb|ip rule save| has the same arguments as +\verb|ip rule show|. + +\paragraph{Example:} This saves all the rules to the {\tt saved\_rules} +file: +\begin{verbatim} +dan@caffeine:~ # ip rule save > saved_rules +\end{verbatim} + +\paragraph{Output format:} The format of the data stream provided by +\verb|ip rule save| is that of \verb|rtnetlink|. See +\verb|rtnetlink(7)| for more information. + +\subsection{{\tt ip rule restore} -- restore rules tables} +\label{IP-RULE-RESTORE} + +\paragraph{Description:} this command restores the contents of the rules +tables according to a data stream as provided by \verb|ip rule save| via +standard input. Note that any rules already in the table are left unchanged, +and duplicates are not ignored. + +\paragraph{Arguments:} This command takes no arguments. + +\paragraph{Example:} This restores all rules that were saved to the +{\tt saved\_rules} file: + +\begin{verbatim} +dan@caffeine:~ # ip rule restore < saved_rules +\end{verbatim} + \section{{\tt ip maddress} --- multicast addresses management} diff --git a/ip/iprule.c b/ip/iprule.c index 2fa9ade9..cec29246 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "rt_names.h" #include "utils.h" @@ -32,7 +33,8 @@ static void usage(void) __attribute__((noreturn)); static void usage(void) { - fprintf(stderr, "Usage: ip rule [ list | add | del | flush ] SELECTOR ACTION\n"); + fprintf(stderr, "Usage: ip rule [ list | add | del | flush | save ] SELECTOR ACTION\n"); + fprintf(stderr, " ip rule restore\n"); fprintf(stderr, "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n"); fprintf(stderr, " [ iif STRING ] [ oif STRING ] [ pref NUMBER ]\n"); fprintf(stderr, "ACTION := [ table TABLE_ID ]\n"); @@ -205,24 +207,65 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) return 0; } -static int iprule_list(int argc, char **argv) +static __u32 rule_dump_magic = 0x71706986; + +static int save_rule_prep(void) { + int ret; + + if (isatty(STDOUT_FILENO)) { + fprintf(stderr, "Not sending a binary stream to stdout\n"); + return -1; + } + + ret = write(STDOUT_FILENO, &rule_dump_magic, sizeof(rule_dump_magic)); + if (ret != sizeof(rule_dump_magic)) { + fprintf(stderr, "Can't write magic to dump file\n"); + return -1; + } + + return 0; +} + +static int save_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + int ret; + + ret = write(STDOUT_FILENO, n, n->nlmsg_len); + if ((ret > 0) && (ret != n->nlmsg_len)) { + fprintf(stderr, "Short write while saving nlmsg\n"); + ret = -EIO; + } + + return ret == n->nlmsg_len ? 0 : ret; +} + +static int iprule_list_or_save(int argc, char **argv, int save) +{ + rtnl_filter_t filter = print_rule; int af = preferred_family; if (af == AF_UNSPEC) af = AF_INET; if (argc > 0) { - fprintf(stderr, "\"ip rule show\" does not take any arguments.\n"); + fprintf(stderr, "\"ip rule %s\" does not take any arguments.\n", + save ? "save" : "show"); return -1; } + if (save) { + if (save_rule_prep()) + return -1; + filter = save_rule; + } + if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { perror("Cannot send dump request"); return 1; } - if (rtnl_dump_filter(&rth, print_rule, stdout) < 0) { + if (rtnl_dump_filter(&rth, filter, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } @@ -230,6 +273,50 @@ static int iprule_list(int argc, char **argv) return 0; } +static int rule_dump_check_magic(void) +{ + int ret; + __u32 magic = 0; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "Can't restore rule dump from a terminal\n"); + return -1; + } + + ret = fread(&magic, sizeof(magic), 1, stdin); + if (magic != rule_dump_magic) { + fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic); + return -1; + } + + return 0; +} + +static int restore_handler(const struct sockaddr_nl *nl, + struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + int ret; + + n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + + ll_init_map(&rth); + + ret = rtnl_talk(&rth, n, n, sizeof(*n)); + if ((ret < 0) && (errno == EEXIST)) + ret = 0; + + return ret; +} + + +static int iprule_restore(void) +{ + if (rule_dump_check_magic()) + exit(-1); + + exit(rtnl_from_file(stdin, &restore_handler, NULL)); +} static int iprule_modify(int cmd, int argc, char **argv) { @@ -443,11 +530,15 @@ static int iprule_flush(int argc, char **argv) int do_iprule(int argc, char **argv) { if (argc < 1) { - return iprule_list(0, NULL); + return iprule_list_or_save(0, NULL, 0); } else if (matches(argv[0], "list") == 0 || matches(argv[0], "lst") == 0 || matches(argv[0], "show") == 0) { - return iprule_list(argc-1, argv+1); + return iprule_list_or_save(argc-1, argv+1, 0); + } else if (matches(argv[0], "save") == 0) { + return iprule_list_or_save(argc-1, argv+1, 1); + } else if (matches(argv[0], "restore") == 0) { + return iprule_restore(); } else if (matches(argv[0], "add") == 0) { return iprule_modify(RTM_NEWRULE, argc-1, argv+1); } else if (matches(argv[0], "delete") == 0) { diff --git a/man/man8/ip-rule.8 b/man/man8/ip-rule.8 index 6245d8cf..b1d03e79 100644 --- a/man/man8/ip-rule.8 +++ b/man/man8/ip-rule.8 @@ -15,9 +15,12 @@ ip-rule \- routing policy database management .ti -8 .B ip rule -.RB " [ " list " | " add " | " del " | " flush " ]" +.RB " [ " list " | " add " | " del " | " flush " | " save " ]" .I SELECTOR ACTION +.ti -8 +.B ip rule " restore " + .ti -8 .IR SELECTOR " := [ " .B from @@ -265,6 +268,27 @@ This command has no arguments. This command has no arguments. The options list or lst are synonyms with show. +.TP +.B ip rule save +save rules table information to stdout +.RS +This command behaves like +.BR "ip rule show" +except that the output is raw data suitable for passing to +.BR "ip rule restore" . +.RE + +.TP +.B ip rule restore +restore rules table information from stdin +.RS +This command expects to read a data stream as returned from +.BR "ip rule save" . +It will attempt to restore the rules table information exactly as +it was at the time of the save. Any rules already in the table are +left unchanged, and duplicates are not ignored. +.RE + .SH SEE ALSO .br .BR ip (8) From 89bb4c6aca8a3b8fe009ece35f4b7380a28ef44d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 22 Oct 2015 23:36:49 -0700 Subject: [PATCH 2/3] update kernel headers Track upstream --- include/linux/rtnetlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 3fe10b05..32449478 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -160,7 +160,7 @@ struct rtattr { /* Macros to handle rtattributes */ -#define RTA_ALIGNTO 4 +#define RTA_ALIGNTO 4U #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) #define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ (rta)->rta_len >= sizeof(struct rtattr) && \ From d583e88ebc859d6ef5bddffb098fa95158d55c75 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 8 Oct 2015 12:22:39 +0200 Subject: [PATCH 3/3] ip, realms: also allow to pass in raw realms value If get_rt_realms() fails, try to get a possible raw u32 realms value for the u32 RTA_FLOW/FRA_FLOW attribute, as it might be useful to directly configure the hex value itself. And only if that fails, then bail out. The source realm is provided in the upper u16 (mask: 0xffff0000) and the destination realm through the lower u16 part (mask: 0x0000ffff). This can be useful for tc's bpf realm matcher, but also a full hex/mask param can be provided already for matching through iptables' --realm cmdline option, for example. Signed-off-by: Daniel Borkmann --- include/rtm_map.h | 3 +-- ip/iproute.c | 6 +++--- ip/iprule.c | 2 +- ip/rtm_map.c | 10 +++++++++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/rtm_map.h b/include/rtm_map.h index 70bda7d0..d6e5885c 100644 --- a/include/rtm_map.h +++ b/include/rtm_map.h @@ -4,7 +4,6 @@ char *rtnl_rtntype_n2a(int id, char *buf, int len); int rtnl_rtntype_a2n(int *id, char *arg); -int get_rt_realms(__u32 *realms, char *arg); - +int get_rt_realms_or_raw(__u32 *realms, char *arg); #endif /* __RTM_MAP_H__ */ diff --git a/ip/iproute.c b/ip/iproute.c index b137f555..ae86cc0d 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -749,7 +749,7 @@ static int parse_one_nh(struct rtmsg *r, struct rtattr *rta, } else if (matches(*argv, "realms") == 0) { __u32 realm; NEXT_ARG(); - if (get_rt_realms(&realm, *argv)) + if (get_rt_realms_or_raw(&realm, *argv)) invarg("\"realm\" value is invalid\n", *argv); rta_addattr32(rta, 4096, RTA_FLOW, realm); rtnh->rtnh_len += sizeof(struct rtattr) + 4; @@ -1050,7 +1050,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) } else if (matches(*argv, "realms") == 0) { __u32 realm; NEXT_ARG(); - if (get_rt_realms(&realm, *argv)) + if (get_rt_realms_or_raw(&realm, *argv)) invarg("\"realm\" value is invalid\n", *argv); addattr32(&req.n, sizeof(req), RTA_FLOW, realm); } else if (strcmp(*argv, "onlink") == 0) { @@ -1383,7 +1383,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) } else if (matches(*argv, "realms") == 0) { __u32 realm; NEXT_ARG(); - if (get_rt_realms(&realm, *argv)) + if (get_rt_realms_or_raw(&realm, *argv)) invarg("invalid realms\n", *argv); filter.realm = realm; filter.realmmask = ~0U; diff --git a/ip/iprule.c b/ip/iprule.c index cec29246..9923b8eb 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -391,7 +391,7 @@ static int iprule_modify(int cmd, int argc, char **argv) } else if (matches(*argv, "realms") == 0) { __u32 realm; NEXT_ARG(); - if (get_rt_realms(&realm, *argv)) + if (get_rt_realms_or_raw(&realm, *argv)) invarg("invalid realms\n", *argv); addattr32(&req.n, sizeof(req), FRA_FLOW, realm); } else if (matches(*argv, "table") == 0 || diff --git a/ip/rtm_map.c b/ip/rtm_map.c index 21e818b4..1d7d2c7e 100644 --- a/ip/rtm_map.c +++ b/ip/rtm_map.c @@ -93,7 +93,7 @@ int rtnl_rtntype_a2n(int *id, char *arg) return 0; } -int get_rt_realms(__u32 *realms, char *arg) +static int get_rt_realms(__u32 *realms, char *arg) { __u32 realm = 0; char *p = strchr(arg, '/'); @@ -114,3 +114,11 @@ int get_rt_realms(__u32 *realms, char *arg) *realms |= realm; return 0; } + +int get_rt_realms_or_raw(__u32 *realms, char *arg) +{ + if (!get_rt_realms(realms, arg)) + return 0; + + return get_unsigned(realms, arg, 0); +}