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