+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ if (action == RTACTION_ADD)
+ xioctl(skfd, SIOCADDRT, rt);
+ else
+ xioctl(skfd, SIOCDELRT, rt);
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static NOINLINE void INET6_setroute(int action, char **args)
+{
+ struct sockaddr_in6 sa6;
+ struct in6_rtmsg rt;
+ int prefix_len, skfd;
+ const char *devname;
+
+ /* We know args isn't NULL from the check in route_main. */
+ const char *target = *args++;
+
+ if (strcmp(target, "default") == 0) {
+ prefix_len = 0;
+ memset(&sa6, 0, sizeof(sa6));
+ } else {
+ char *cp;
+ cp = strchr(target, '/'); /* Yes... const to non is ok. */
+ if (cp) {
+ *cp = '\0';
+ prefix_len = xatoul_range(cp + 1, 0, 128);
+ } else {
+ prefix_len = 128;
+ }
+ if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset(&rt, 0, sizeof(rt));
+
+ memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+ /* Fill in the other fields. */
+ rt.rtmsg_dst_len = prefix_len;
+ rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
+ rt.rtmsg_metric = 1;
+
+ devname = NULL;
+
+ while (*args) {
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
+
+ if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
+ rt.rtmsg_flags |= flags_ipvx[k & 3];
+ continue;
+ }
+
+ if (k == KW_IPVx_METRIC) {
+ rt.rtmsg_metric = xatoul(args_m1);
+ continue;
+ }
+
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rtmsg_flags & RTF_GATEWAY) {
+ bb_show_usage();
+ }
+
+ if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
+ }
+ memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ rt.rtmsg_flags |= RTF_GATEWAY;
+ continue;
+ }
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't require the dev/device keyword in that case. */
+ if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ devname = args[-1];
+ continue;
+ }
+
+ /* Nothing matched. */
+ bb_show_usage();
+ }
+
+ /* Create a socket to the INET6 kernel. */
+ skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
+
+ rt.rtmsg_ifindex = 0;
+
+ if (devname) {
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy_IFNAMSIZ(ifr.ifr_name, devname);
+ xioctl(skfd, SIOCGIFINDEX, &ifr);
+ rt.rtmsg_ifindex = ifr.ifr_ifindex;