/* * inet6_sr.c This files contains INET6 related route manipulation methods. * * Part of net-tools, the Linux base networking tools * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ #include "config.h" #if HAVE_AFINET6 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "version.h" #include "net-support.h" #include "pathnames.h" #include "intl.h" #include "net-features.h" #include "util.h" extern struct aftype inet6_aftype; static int skfd = -1; static int usage(const int rc) { fprintf(stderr, _("Usage: inet6_route [-vF] del Target\n")); fprintf(stderr, _(" inet6_route [-vF] add Target [gw Gw] [metric M] [[dev] If]\n")); fprintf(stderr, _(" inet6_route [-FC] flush NOT supported\n")); return (rc); } static int INET6_setroute(int action, int options, char **args) { struct in6_rtmsg rt; struct ifreq ifr; struct sockaddr_storage sas; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sas; char target[128], gateway[128] = "NONE"; int metric, prefix_len; char *devname = NULL; char *cp; if (*args == NULL) return usage(E_OPTERR); safe_strncpy(target, *args++, sizeof(target)); if (!strcmp(target, "default")) { prefix_len = 0; memset(&sas, 0, sizeof(sas)); } else { if ((cp = strchr(target, '/'))) { prefix_len = atol(cp + 1); if ((prefix_len < 0) || (prefix_len > 128)) return usage(E_OPTERR); *cp = 0; } else { prefix_len = 128; } if (inet6_aftype.input(1, target, &sas) < 0 && inet6_aftype.input(0, target, &sas) < 0) { inet6_aftype.herror(target); return (1); } } /* Clean out the RTREQ structure. */ memset((char *) &rt, 0, sizeof(struct in6_rtmsg)); memcpy(&rt.rtmsg_dst, sin6->sin6_addr.s6_addr, sizeof(struct in6_addr)); /* Fill in the other fields. */ rt.rtmsg_flags = RTF_UP; if (prefix_len == 128) rt.rtmsg_flags |= RTF_HOST; rt.rtmsg_metric = 1; rt.rtmsg_dst_len = prefix_len; while (*args) { if (!strcmp(*args, "metric")) { args++; if (!*args || !isdigit(**args)) return usage(E_OPTERR); metric = atoi(*args); rt.rtmsg_metric = metric; args++; continue; } if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) { args++; if (!*args) return usage(E_OPTERR); if (rt.rtmsg_flags & RTF_GATEWAY) return usage(E_OPTERR); safe_strncpy(gateway, *args, sizeof(gateway)); if (inet6_aftype.input(1, gateway, &sas) < 0) { inet6_aftype.herror(gateway); return (E_LOOKUP); } memcpy(&rt.rtmsg_gateway, sin6->sin6_addr.s6_addr, sizeof(struct in6_addr)); rt.rtmsg_flags |= RTF_GATEWAY; args++; continue; } if (!strcmp(*args, "mod")) { args++; rt.rtmsg_flags |= RTF_MODIFIED; continue; } if (!strcmp(*args, "dyn")) { args++; rt.rtmsg_flags |= RTF_DYNAMIC; continue; } if (!strcmp(*args, "device") || !strcmp(*args, "dev")) { args++; if (!*args) return usage(E_OPTERR); } else if (args[1]) return usage(E_OPTERR); devname = *args; args++; } /* Create a socket to the INET6 kernel. */ if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { perror("socket"); return (E_SOCK); } if (devname) { memset(&ifr, 0, sizeof(ifr)); safe_strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) { perror("SIOGIFINDEX"); return (E_SOCK); } rt.rtmsg_ifindex = ifr.ifr_ifindex; } else rt.rtmsg_ifindex = 0; /* Tell the kernel to accept this route. */ if (action == RTACTION_DEL) { if (ioctl(skfd, SIOCDELRT, &rt) < 0) { perror("SIOCDELRT"); close(skfd); return (E_SOCK); } } else { if (ioctl(skfd, SIOCADDRT, &rt) < 0) { perror("SIOCADDRT"); close(skfd); return (E_SOCK); } } /* Close the socket. */ (void) close(skfd); return (0); } int INET6_rinput(int action, int options, char **args) { if (action == RTACTION_FLUSH) { fprintf(stderr, _("Flushing `inet6' routing table not supported\n")); return usage(E_OPTERR); } if (action == RTACTION_HELP) return usage(E_USAGE); return (INET6_setroute(action, options, args)); } #endif /* HAVE_AFINET6 */