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 * 11 * 12 * Changes: 13 * 14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <unistd.h> 20#include <syslog.h> 21#include <fcntl.h> 22#include <sys/socket.h> 23#include <netinet/in.h> 24#include <netinet/ip.h> 25#include <arpa/inet.h> 26#include <string.h> 27 28#include "rt_names.h" 29#include "utils.h" 30 31extern struct rtnl_handle rth; 32 33static void usage(void) __attribute__((noreturn)); 34 35static void usage(void) 36{ 37 fprintf(stderr, "Usage: ip rule [ list | add | del | flush ] SELECTOR ACTION\n"); 38 fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n"); 39 fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n"); 40 fprintf(stderr, "ACTION := [ table TABLE_ID ]\n"); 41 fprintf(stderr, " [ prohibit | reject | unreachable ]\n"); 42 fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); 43 fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); 44 exit(-1); 45} 46 47static int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, 48 void *arg) 49{ 50 FILE *fp = (FILE*)arg; 51 struct rtmsg *r = NLMSG_DATA(n); 52 int len = n->nlmsg_len; 53 int host_len = -1; 54 struct rtattr * tb[RTA_MAX+1]; 55 char abuf[256]; 56 SPRINT_BUF(b1); 57 58 if (n->nlmsg_type != RTM_NEWRULE) 59 return 0; 60 61 len -= NLMSG_LENGTH(sizeof(*r)); 62 if (len < 0) 63 return -1; 64 65 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); 66 67 if (r->rtm_family == AF_INET) 68 host_len = 32; 69 else if (r->rtm_family == AF_INET6) 70 host_len = 128; 71 else if (r->rtm_family == AF_DECnet) 72 host_len = 16; 73 else if (r->rtm_family == AF_IPX) 74 host_len = 80; 75 76 if (tb[RTA_PRIORITY]) 77 fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])); 78 else 79 fprintf(fp, "0:\t"); 80 81 if (tb[RTA_SRC]) { 82 if (r->rtm_src_len != host_len) { 83 fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, 84 RTA_PAYLOAD(tb[RTA_SRC]), 85 RTA_DATA(tb[RTA_SRC]), 86 abuf, sizeof(abuf)), 87 r->rtm_src_len 88 ); 89 } else { 90 fprintf(fp, "from %s ", format_host(r->rtm_family, 91 RTA_PAYLOAD(tb[RTA_SRC]), 92 RTA_DATA(tb[RTA_SRC]), 93 abuf, sizeof(abuf)) 94 ); 95 } 96 } else if (r->rtm_src_len) { 97 fprintf(fp, "from 0/%d ", r->rtm_src_len); 98 } else { 99 fprintf(fp, "from all "); 100 } 101 102 if (tb[RTA_DST]) { 103 if (r->rtm_dst_len != host_len) { 104 fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, 105 RTA_PAYLOAD(tb[RTA_DST]), 106 RTA_DATA(tb[RTA_DST]), 107 abuf, sizeof(abuf)), 108 r->rtm_dst_len 109 ); 110 } else { 111 fprintf(fp, "to %s ", format_host(r->rtm_family, 112 RTA_PAYLOAD(tb[RTA_DST]), 113 RTA_DATA(tb[RTA_DST]), 114 abuf, sizeof(abuf))); 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); 122 fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); 123 } 124 if (tb[RTA_PROTOINFO]) { 125 fprintf(fp, "fwmark %#x ", *(__u32*)RTA_DATA(tb[RTA_PROTOINFO])); 126 } 127 128 if (tb[RTA_IIF]) { 129 fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF])); 130 } 131 132 if (r->rtm_table) 133 fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); 134 135 if (tb[RTA_FLOW]) { 136 __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); 137 __u32 from = to>>16; 138 to &= 0xFFFF; 139 if (from) { 140 fprintf(fp, "realms %s/", 141 rtnl_rtrealm_n2a(from, b1, sizeof(b1))); 142 } 143 fprintf(fp, "%s ", 144 rtnl_rtrealm_n2a(to, b1, sizeof(b1))); 145 } 146 147 if (r->rtm_type == RTN_NAT) { 148 if (tb[RTA_GATEWAY]) { 149 fprintf(fp, "map-to %s ", 150 format_host(r->rtm_family, 151 RTA_PAYLOAD(tb[RTA_GATEWAY]), 152 RTA_DATA(tb[RTA_GATEWAY]), 153 abuf, sizeof(abuf))); 154 } else 155 fprintf(fp, "masquerade"); 156 } else if (r->rtm_type != RTN_UNICAST) 157 fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); 158 159 fprintf(fp, "\n"); 160 fflush(fp); 161 return 0; 162} 163 164static int iprule_list(int argc, char **argv) 165{ 166 int af = preferred_family; 167 168 if (af == AF_UNSPEC) 169 af = AF_INET; 170 171 if (argc > 0) { 172 fprintf(stderr, "\"ip rule show\" does not take any arguments.\n"); 173 return -1; 174 } 175 176 if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { 177 perror("Cannot send dump request"); 178 return 1; 179 } 180 181 if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) { 182 fprintf(stderr, "Dump terminated\n"); 183 return 1; 184 } 185 186 return 0; 187} 188 189 190static int iprule_modify(int cmd, int argc, char **argv) 191{ 192 int table_ok = 0; 193 struct { 194 struct nlmsghdr n; 195 struct rtmsg r; 196 char buf[1024]; 197 } req; 198 199 memset(&req, 0, sizeof(req)); 200 201 req.n.nlmsg_type = cmd; 202 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); 203 req.n.nlmsg_flags = NLM_F_REQUEST; 204 req.r.rtm_family = preferred_family; 205 req.r.rtm_protocol = RTPROT_BOOT; 206 req.r.rtm_scope = RT_SCOPE_UNIVERSE; 207 req.r.rtm_table = 0; 208 req.r.rtm_type = RTN_UNSPEC; 209 210 if (cmd == RTM_NEWRULE) { 211 req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; 212 req.r.rtm_type = RTN_UNICAST; 213 } 214 215 while (argc > 0) { 216 if (strcmp(*argv, "from") == 0) { 217 inet_prefix dst; 218 NEXT_ARG(); 219 get_prefix(&dst, *argv, req.r.rtm_family); 220 req.r.rtm_src_len = dst.bitlen; 221 addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen); 222 } else if (strcmp(*argv, "to") == 0) { 223 inet_prefix dst; 224 NEXT_ARG(); 225 get_prefix(&dst, *argv, req.r.rtm_family); 226 req.r.rtm_dst_len = dst.bitlen; 227 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); 228 } else if (matches(*argv, "preference") == 0 || 229 matches(*argv, "order") == 0 || 230 matches(*argv, "priority") == 0) { 231 __u32 pref; 232 NEXT_ARG(); 233 if (get_u32(&pref, *argv, 0)) 234 invarg("preference value is invalid\n", *argv); 235 addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref); 236 } else if (strcmp(*argv, "tos") == 0) { 237 __u32 tos; 238 NEXT_ARG(); 239 if (rtnl_dsfield_a2n(&tos, *argv)) 240 invarg("TOS value is invalid\n", *argv); 241 req.r.rtm_tos = tos; 242 } else if (strcmp(*argv, "fwmark") == 0) { 243 __u32 fwmark; 244 NEXT_ARG(); 245 if (get_u32(&fwmark, *argv, 0)) 246 invarg("fwmark value is invalid\n", *argv); 247 addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); 248 } else if (matches(*argv, "realms") == 0) { 249 __u32 realm; 250 NEXT_ARG(); 251 if (get_rt_realms(&realm, *argv)) 252 invarg("invalid realms\n", *argv); 253 addattr32(&req.n, sizeof(req), RTA_FLOW, realm); 254 } else if (matches(*argv, "table") == 0 || 255 strcmp(*argv, "lookup") == 0) { 256 __u32 tid; 257 NEXT_ARG(); 258 if (rtnl_rttable_a2n(&tid, *argv)) 259 invarg("invalid table ID\n", *argv); 260 req.r.rtm_table = tid; 261 table_ok = 1; 262 } else if (strcmp(*argv, "dev") == 0 || 263 strcmp(*argv, "iif") == 0) { 264 NEXT_ARG(); 265 addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1); 266 } else if (strcmp(*argv, "nat") == 0 || 267 matches(*argv, "map-to") == 0) { 268 NEXT_ARG(); 269 fprintf(stderr, "Warning: route NAT is deprecated\n"); 270 addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); 271 req.r.rtm_type = RTN_NAT; 272 } else { 273 int type; 274 275 if (strcmp(*argv, "type") == 0) { 276 NEXT_ARG(); 277 } 278 if (matches(*argv, "help") == 0) 279 usage(); 280 if (rtnl_rtntype_a2n(&type, *argv)) 281 invarg("Failed to parse rule type", *argv); 282 req.r.rtm_type = type; 283 } 284 argc--; 285 argv++; 286 } 287 288 if (req.r.rtm_family == AF_UNSPEC) 289 req.r.rtm_family = AF_INET; 290 291 if (!table_ok && cmd == RTM_NEWRULE) 292 req.r.rtm_table = RT_TABLE_MAIN; 293 294 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) 295 return 2; 296 297 return 0; 298} 299 300 301static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 302{ 303 struct rtmsg *r = NLMSG_DATA(n); 304 int len = n->nlmsg_len; 305 struct rtattr * tb[RTA_MAX+1]; 306 307 len -= NLMSG_LENGTH(sizeof(*r)); 308 if (len < 0) 309 return -1; 310 311 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); 312 313 if (tb[RTA_PRIORITY]) { 314 n->nlmsg_type = RTM_DELRULE; 315 n->nlmsg_flags = NLM_F_REQUEST; 316 317 if (rtnl_talk(&rth, n, 0, 0, NULL, NULL, NULL) < 0) 318 return -2; 319 } 320 321 return 0; 322} 323 324static int iprule_flush(int argc, char **argv) 325{ 326 int af = preferred_family; 327 328 if (af == AF_UNSPEC) 329 af = AF_INET; 330 331 if (argc > 0) { 332 fprintf(stderr, "\"ip rule flush\" does not allow arguments\n"); 333 return -1; 334 } 335 336 if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { 337 perror("Cannot send dump request"); 338 return 1; 339 } 340 341 if (rtnl_dump_filter(&rth, flush_rule, NULL, NULL, NULL) < 0) { 342 fprintf(stderr, "Flush terminated\n"); 343 return 1; 344 } 345 346 return 0; 347} 348 349int do_iprule(int argc, char **argv) 350{ 351 if (argc < 1) { 352 return iprule_list(0, NULL); 353 } else if (matches(argv[0], "list") == 0 || 354 matches(argv[0], "lst") == 0 || 355 matches(argv[0], "show") == 0) { 356 return iprule_list(argc-1, argv+1); 357 } else if (matches(argv[0], "add") == 0) { 358 return iprule_modify(RTM_NEWRULE, argc-1, argv+1); 359 } else if (matches(argv[0], "delete") == 0) { 360 return iprule_modify(RTM_DELRULE, argc-1, argv+1); 361 } else if (matches(argv[0], "flush") == 0) { 362 return iprule_flush(argc-1, argv+1); 363 } else if (matches(argv[0], "help") == 0) 364 usage(); 365 366 fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); 367 exit(-1); 368} 369 370