grep: add proper support for pattern_list
[oweals/busybox.git] / networking / libiproute / iprule.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License
5  * as published by the Free Software Foundation; either version
6  * 2 of the License, or (at your option) any later version.
7  *
8  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
9  *
10  * Changes:
11  *
12  * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
13  * initially integrated into busybox by Bernhard Reutner-Fischer
14  */
15 #include <netinet/in.h>
16 #include <netinet/ip.h>
17 #include <arpa/inet.h>
18
19 /* from <linux/fib_rules.h>: */
20 #define FRA_SUPPRESS_IFGROUP   13
21 #define FRA_SUPPRESS_PREFIXLEN 14
22
23 #include "ip_common.h"  /* #include "libbb.h" is inside */
24 #include "rt_names.h"
25 #include "utils.h"
26
27 #include <linux/version.h>
28 /* RTA_TABLE is not a define, can't test with ifdef. */
29 /* As a proxy, test which kernels toolchain expects: */
30 #define HAVE_RTA_TABLE (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
31
32 /* If you add stuff here, update iprule_full_usage */
33 static const char keywords[] ALIGN1 =
34         "from\0""to\0""preference\0""order\0""priority\0"
35         "tos\0""fwmark\0""realms\0""table\0""lookup\0"
36         "suppress_prefixlength\0""suppress_ifgroup\0"
37         "dev\0""iif\0""nat\0""map-to\0""type\0""help\0"
38         ;
39 #define keyword_preference            (keywords           + sizeof("from") + sizeof("to"))
40 #define keyword_fwmark                (keyword_preference + sizeof("preference") + sizeof("order") + sizeof("priority") + sizeof("tos"))
41 #define keyword_realms                (keyword_fwmark     + sizeof("fwmark"))
42 #define keyword_suppress_prefixlength (keyword_realms     + sizeof("realms") + sizeof("table") + sizeof("lookup"))
43 #define keyword_suppress_ifgroup      (keyword_suppress_prefixlength + sizeof("suppress_prefixlength"))
44 enum {
45         ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
46         ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup,
47         ARG_suppress_prefixlength, ARG_suppress_ifgroup,
48         ARG_dev, ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help,
49 };
50
51 static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
52                                         struct nlmsghdr *n, void *arg UNUSED_PARAM)
53 {
54         struct rtmsg *r = NLMSG_DATA(n);
55         int len = n->nlmsg_len;
56         int host_len = -1;
57         struct rtattr * tb[RTA_MAX+1];
58
59         if (n->nlmsg_type != RTM_NEWRULE)
60                 return 0;
61
62         len -= NLMSG_LENGTH(sizeof(*r));
63         if (len < 0)
64                 return -1;
65
66         //memset(tb, 0, sizeof(tb)); - parse_rtattr does this
67         parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
68
69         if (r->rtm_family == AF_INET)
70                 host_len = 32;
71         else if (r->rtm_family == AF_INET6)
72                 host_len = 128;
73 /*      else if (r->rtm_family == AF_DECnet)
74                 host_len = 16;
75         else if (r->rtm_family == AF_IPX)
76                 host_len = 80;
77 */
78         printf("%u:\t", tb[RTA_PRIORITY] ?
79                                         *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])
80                                         : 0);
81         printf("from ");
82         if (tb[RTA_SRC]) {
83                 if (r->rtm_src_len != host_len) {
84                         printf("%s/%u",
85                                 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
86                                 r->rtm_src_len
87                         );
88                 } else {
89                         fputs(format_host(r->rtm_family,
90                                                 RTA_PAYLOAD(tb[RTA_SRC]),
91                                                 RTA_DATA(tb[RTA_SRC])),
92                                 stdout
93                         );
94                 }
95         } else if (r->rtm_src_len) {
96                 printf("0/%d", r->rtm_src_len);
97         } else {
98                 printf("all");
99         }
100         bb_putchar(' ');
101
102         if (tb[RTA_DST]) {
103                 if (r->rtm_dst_len != host_len) {
104                         printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
105                                                          RTA_DATA(tb[RTA_DST])),
106                                 r->rtm_dst_len
107                                 );
108                 } else {
109                         printf("to %s ", format_host(r->rtm_family,
110                                                        RTA_PAYLOAD(tb[RTA_DST]),
111                                                        RTA_DATA(tb[RTA_DST])));
112                 }
113         } else if (r->rtm_dst_len) {
114                 printf("to 0/%d ", r->rtm_dst_len);
115         }
116
117         if (r->rtm_tos) {
118                 printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos));
119         }
120         if (tb[RTA_PROTOINFO]) {
121                 printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
122         }
123
124         if (tb[RTA_IIF]) {
125                 printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
126         }
127
128 #if HAVE_RTA_TABLE
129         if (tb[RTA_TABLE])
130                 printf("lookup %s ", rtnl_rttable_n2a(*(uint32_t*)RTA_DATA(tb[RTA_TABLE])));
131         else
132 #endif
133         if (r->rtm_table)
134                 printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table));
135
136         if (tb[FRA_SUPPRESS_PREFIXLEN]) {
137                 int pl = *(uint32_t*)RTA_DATA(tb[FRA_SUPPRESS_PREFIXLEN]);
138                 if (pl != -1)
139                         printf("%s %d ", keyword_suppress_prefixlength, pl);
140         }
141         if (tb[FRA_SUPPRESS_IFGROUP]) {
142                 int grp = *(uint32_t*)RTA_DATA(tb[FRA_SUPPRESS_IFGROUP]);
143                 if (grp != -1)
144                         printf("%s %d ", keyword_suppress_ifgroup, grp);
145         }
146
147         if (tb[RTA_FLOW]) {
148                 uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
149                 uint32_t from = to>>16;
150                 to &= 0xFFFF;
151                 if (from) {
152                         printf("realms %s/",
153                                 rtnl_rtrealm_n2a(from));
154                 }
155                 printf("%s ",
156                         rtnl_rtrealm_n2a(to));
157         }
158
159         if (r->rtm_type == RTN_NAT) {
160                 if (tb[RTA_GATEWAY]) {
161                         printf("map-to %s ",
162                                 format_host(r->rtm_family,
163                                             RTA_PAYLOAD(tb[RTA_GATEWAY]),
164                                             RTA_DATA(tb[RTA_GATEWAY]))
165                         );
166                 } else
167                         printf("masquerade");
168         } else if (r->rtm_type != RTN_UNICAST)
169                 fputs(rtnl_rtntype_n2a(r->rtm_type), stdout);
170
171         bb_putchar('\n');
172         /*fflush_all();*/
173         return 0;
174 }
175
176 /* Return value becomes exitcode. It's okay to not return at all */
177 static int iprule_list(char **argv)
178 {
179         struct rtnl_handle rth;
180         int af = preferred_family;
181
182         if (af == AF_UNSPEC)
183                 af = AF_INET;
184
185         if (*argv) {
186                 //bb_error_msg("\"rule show\" needs no arguments");
187                 bb_warn_ignoring_args(*argv);
188                 return -1;
189         }
190
191         xrtnl_open(&rth);
192
193         xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
194         xrtnl_dump_filter(&rth, print_rule, NULL);
195
196         return 0;
197 }
198
199 /* Return value becomes exitcode. It's okay to not return at all */
200 static int iprule_modify(int cmd, char **argv)
201 {
202         bool table_ok = 0;
203         struct rtnl_handle rth;
204         struct {
205                 struct nlmsghdr n;
206                 struct rtmsg    r;
207                 char            buf[1024];
208         } req;
209         smalluint key;
210
211         memset(&req, 0, sizeof(req));
212
213         req.n.nlmsg_type = cmd;
214         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
215         req.n.nlmsg_flags = NLM_F_REQUEST;
216         req.r.rtm_family = preferred_family;
217         req.r.rtm_protocol = RTPROT_BOOT;
218         if (RT_SCOPE_UNIVERSE != 0)
219                 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
220         /*req.r.rtm_table = 0; - already is */
221         if (RTN_UNSPEC != 0)
222                 req.r.rtm_type = RTN_UNSPEC;
223
224         if (cmd == RTM_NEWRULE) {
225                 req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
226                 req.r.rtm_type = RTN_UNICAST;
227         }
228
229         while (*argv) {
230                 key = index_in_substrings(keywords, *argv) + 1;
231                 if (key == 0) /* no match found in keywords array, bail out. */
232                         invarg_1_to_2(*argv, applet_name);
233                 if (key == ARG_from) {
234                         inet_prefix dst;
235                         NEXT_ARG();
236                         get_prefix(&dst, *argv, req.r.rtm_family);
237                         req.r.rtm_src_len = dst.bitlen;
238                         addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
239                 } else if (key == ARG_to) {
240                         inet_prefix dst;
241                         NEXT_ARG();
242                         get_prefix(&dst, *argv, req.r.rtm_family);
243                         req.r.rtm_dst_len = dst.bitlen;
244                         addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
245                 } else if (key == ARG_preference ||
246                            key == ARG_order ||
247                            key == ARG_priority
248                 ) {
249                         uint32_t pref;
250                         NEXT_ARG();
251                         pref = get_u32(*argv, keyword_preference);
252                         addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
253                 } else if (key == ARG_tos) {
254                         uint32_t tos;
255                         NEXT_ARG();
256                         if (rtnl_dsfield_a2n(&tos, *argv))
257                                 invarg_1_to_2(*argv, "TOS");
258                         req.r.rtm_tos = tos;
259                 } else if (key == ARG_fwmark) {
260                         uint32_t fwmark;
261                         NEXT_ARG();
262                         fwmark = get_u32(*argv, keyword_fwmark);
263                         addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
264                 } else if (key == ARG_realms) {
265                         uint32_t realm;
266                         NEXT_ARG();
267                         if (get_rt_realms(&realm, *argv))
268                                 invarg_1_to_2(*argv, keyword_realms);
269                         addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
270                 } else if (key == ARG_table ||
271                            key == ARG_lookup
272                 ) {
273                         uint32_t tid;
274                         NEXT_ARG();
275                         if (rtnl_rttable_a2n(&tid, *argv))
276                                 invarg_1_to_2(*argv, "table ID");
277
278 #if HAVE_RTA_TABLE
279                         if (tid > 255) {
280                                 req.r.rtm_table = RT_TABLE_UNSPEC;
281                                 addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
282                         } else
283 #endif
284                                 req.r.rtm_table = tid;
285
286                         table_ok = 1;
287                 } else if (key == ARG_suppress_prefixlength) {
288                         int prefix_length;
289                         NEXT_ARG();
290                         prefix_length = get_u32(*argv, keyword_suppress_prefixlength);
291                         addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, prefix_length);
292                 } else if (key == ARG_suppress_ifgroup) {
293                         int grp;
294                         NEXT_ARG();
295                         grp = get_u32(*argv, keyword_suppress_ifgroup);
296                         addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, grp);
297                 } else if (key == ARG_dev ||
298                            key == ARG_iif
299                 ) {
300                         NEXT_ARG();
301                         addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
302                 } else if (key == ARG_nat ||
303                            key == ARG_map_to
304                 ) {
305                         NEXT_ARG();
306                         addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
307                         req.r.rtm_type = RTN_NAT;
308                 } else {
309                         int type;
310
311                         if (key == ARG_type) {
312                                 NEXT_ARG();
313                         }
314                         if (key == ARG_help)
315                                 bb_show_usage();
316                         if (rtnl_rtntype_a2n(&type, *argv))
317                                 invarg_1_to_2(*argv, "type");
318                         req.r.rtm_type = type;
319                 }
320                 argv++;
321         }
322
323         if (req.r.rtm_family == AF_UNSPEC)
324                 req.r.rtm_family = AF_INET;
325
326         if (!table_ok && cmd == RTM_NEWRULE)
327                 req.r.rtm_table = RT_TABLE_MAIN;
328
329         xrtnl_open(&rth);
330
331         if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
332                 return 2;
333
334         return 0;
335 }
336
337 /* Return value becomes exitcode. It's okay to not return at all */
338 int FAST_FUNC do_iprule(char **argv)
339 {
340         static const char ip_rule_commands[] ALIGN1 =
341                 "add\0""delete\0""list\0""show\0";
342         if (*argv) {
343                 int cmd = index_in_substrings(ip_rule_commands, *argv);
344                 if (cmd < 0)
345                         invarg_1_to_2(*argv, applet_name);
346                 argv++;
347                 if (cmd < 2)
348                         return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
349         }
350         return iprule_list(argv);
351 }