| @@ -19,6 +19,8 @@ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| + | #include <fcntl.h> |
| + | #include <unistd.h> |
| |
| #include "rt_names.h" |
| #include "utils.h" |
| @@ -33,6 +35,8 @@ |
| static struct |
| { |
| int tb; |
| + | int flushed; |
| + | char *flushb; |
| int flushp; |
| int flushe; |
| struct rtnl_handle *rth; |
| @@ -51,6 +55,16 @@ static struct |
| inet_prefix msrc; |
| } filter; |
| |
| + | static int flush_update(void) |
| + | { |
| + | if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { |
| + | perror("Failed to send flush request\n"); |
| + | return -1; |
| + | } |
| + | filter.flushp = 0; |
| + | return 0; |
| + | } |
| + | |
| static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| { |
| FILE *fp = (FILE*)arg; |
| @@ -67,6 +81,8 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); |
| return 0; |
| } |
| + | if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) |
| + | return 0; |
| len -= NLMSG_LENGTH(sizeof(*r)); |
| if (len < 0) { |
| error_msg("wrong nlmsg len %d", len); |
| @@ -128,6 +144,30 @@ static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| memset(tb, 0, sizeof(tb)); |
| parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); |
| |
| + | if (filter.flushb && |
| + | r->rtm_family == AF_INET6 && |
| + | r->rtm_dst_len == 0 && |
| + | r->rtm_type == RTN_UNREACHABLE && |
| + | tb[RTA_PRIORITY] && |
| + | *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) |
| + | return 0; |
| + | |
| + | if (filter.flushb) { |
| + | struct nlmsghdr *fn; |
| + | if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { |
| + | if (flush_update()) |
| + | return -1; |
| + | } |
| + | fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); |
| + | memcpy(fn, n, n->nlmsg_len); |
| + | fn->nlmsg_type = RTM_DELROUTE; |
| + | fn->nlmsg_flags = NLM_F_REQUEST; |
| + | fn->nlmsg_seq = ++filter.rth->seq; |
| + | filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; |
| + | filter.flushed++; |
| + | return 0; |
| + | } |
| + | |
| if (n->nlmsg_type == RTM_DELROUTE) { |
| fprintf(fp, "Deleted "); |
| } |
| @@ -393,6 +433,29 @@ static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) |
| return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); |
| } |
| |
| + | static int iproute_flush_cache(void) |
| + | { |
| + | #define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" |
| + | |
| + | int len; |
| + | int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); |
| + | char *buffer = "-1"; |
| + | |
| + | if (flush_fd < 0) { |
| + | fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); |
| + | return -1; |
| + | } |
| + | |
| + | len = strlen (buffer); |
| + | |
| + | if ((write (flush_fd, (void *)buffer, len)) < len) { |
| + | fprintf (stderr, "Cannot flush routing cache\n"); |
| + | return -1; |
| + | } |
| + | close(flush_fd); |
| + | return 0; |
| + | } |
| + | |
| static void iproute_reset_filter(void) |
| { |
| memset(&filter, 0, sizeof(filter)); |
| @@ -400,7 +463,7 @@ static void iproute_reset_filter(void) |
| filter.msrc.bitlen = -1; |
| } |
| |
| - | static int iproute_list(int argc, char **argv) |
| + | static int iproute_list_or_flush(int argc, char **argv, int flush) |
| { |
| int do_ipv6 = preferred_family; |
| struct rtnl_handle rth; |
| @@ -410,6 +473,11 @@ static int iproute_list(int argc, char **argv) |
| iproute_reset_filter(); |
| filter.tb = RT_TABLE_MAIN; |
| |
| + | if (flush && argc <= 0) { |
| + | fprintf(stderr, "\"ip route flush\" requires arguments.\n"); |
| + | return -1; |
| + | } |
| + | |
| while (argc > 0) { |
| if (matches(*argv, "protocol") == 0) { |
| int prot = 0; |
| @@ -496,6 +564,46 @@ static int iproute_list(int argc, char **argv) |
| } |
| } |
| |
| + | if (flush) { |
| + | int round = 0; |
| + | char flushb[4096-512]; |
| + | |
| + | if (filter.tb == -1) { |
| + | if (do_ipv6 != AF_INET6) |
| + | iproute_flush_cache(); |
| + | if (do_ipv6 == AF_INET) |
| + | return 0; |
| + | } |
| + | |
| + | filter.flushb = flushb; |
| + | filter.flushp = 0; |
| + | filter.flushe = sizeof(flushb); |
| + | filter.rth = &rth; |
| + | |
| + | for (;;) { |
| + | if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { |
| + | perror("Cannot send dump request"); |
| + | return -1; |
| + | } |
| + | filter.flushed = 0; |
| + | if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { |
| + | error_msg("Flush terminated\n"); |
| + | return -1; |
| + | } |
| + | if (filter.flushed == 0) { |
| + | if (round == 0) { |
| + | if (filter.tb != -1 || do_ipv6 == AF_INET6) |
| + | fprintf(stderr, "Nothing to flush.\n"); |
| + | } |
| + | fflush(stdout); |
| + | return 0; |
| + | } |
| + | round++; |
| + | if (flush_update() < 0) |
| + | exit(1); |
| + | } |
| + | } |
| + | |
| if (filter.tb != -1) { |
| if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { |
| perror_msg_and_die("Cannot send dump request"); |
| @@ -686,7 +794,7 @@ static int iproute_get(int argc, char **argv) |
| int do_iproute(int argc, char **argv) |
| { |
| const char *ip_route_commands[] = { "add", "append", "change", "chg", |
| - | "delete", "get", "list", "show", "prepend", "replace", "test", 0 }; |
| + | "delete", "get", "list", "show", "prepend", "replace", "test", "flush", 0 }; |
| unsigned short command_num = 6; |
| unsigned int flags = 0; |
| int cmd = RTM_NEWROUTE; |
| @@ -712,13 +820,15 @@ int do_iproute(int argc, char **argv) |
| return iproute_get(argc-1, argv+1); |
| case 6: /* list */ |
| case 7: /* show */ |
| - | return iproute_list(argc-1, argv+1); |
| + | return iproute_list_or_flush(argc-1, argv+1, 0); |
| case 8: /* prepend */ |
| flags = NLM_F_CREATE; |
| case 9: /* replace */ |
| flags = NLM_F_CREATE|NLM_F_REPLACE; |
| case 10: /* test */ |
| flags = NLM_F_EXCL; |
| + | case 11: /* flush */ |
| + | return iproute_list_or_flush(argc-1, argv+1, 1); |
| default: |
| error_msg_and_die("Unknown command %s", *argv); |
| } |