int if_route6(const struct rt6 *rt, int action) { union sockunion { struct sockaddr sa; struct sockaddr_in6 sin; struct sockaddr_dl sdl; struct sockaddr_storage ss; } su; struct rtm { struct rt_msghdr hdr; char buffer[sizeof(su) * 4]; } rtm; char *bp = rtm.buffer; size_t l; int retval = 0; const struct ipv6_addr_l *lla; /* KAME based systems want to store the scope inside the sin6_addr * for link local addreses */ #ifdef __KAME__ #define SCOPE { \ if (IN6_IS_ADDR_LINKLOCAL(&su.sin.sin6_addr)) { \ uint16_t scope = htons(su.sin.sin6_scope_id); \ memcpy(&su.sin.sin6_addr.s6_addr[2], &scope, \ sizeof(scope)); \ su.sin.sin6_scope_id = 0; \ } \ } #else #define SCOPE #endif #define ADDSU { \ l = RT_ROUNDUP(su.sa.sa_len); \ memcpy(bp, &su, l); \ bp += l; \ } #define ADDADDRS(addr, scope) { \ memset(&su, 0, sizeof(su)); \ su.sin.sin6_family = AF_INET6; \ su.sin.sin6_len = sizeof(su.sin); \ (&su.sin)->sin6_addr = *addr; \ su.sin.sin6_scope_id = scope; \ SCOPE; \ ADDSU; \ } #define ADDADDR(addr) ADDADDRS(addr, 0) memset(&rtm, 0, sizeof(rtm)); rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_seq = 1; if (action == 0) rtm.hdr.rtm_type = RTM_CHANGE; else if (action > 0) rtm.hdr.rtm_type = RTM_ADD; else rtm.hdr.rtm_type = RTM_DELETE; rtm.hdr.rtm_flags = RTF_UP; /* None interface subnet routes are static. */ if (IN6_IS_ADDR_UNSPECIFIED(&rt->dest) && IN6_IS_ADDR_UNSPECIFIED(&rt->net)) rtm.hdr.rtm_flags |= RTF_GATEWAY; #ifdef RTF_CLONING else rtm.hdr.rtm_flags |= RTF_CLONING; #endif rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; if (action >= 0) rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA; ADDADDR(&rt->dest); if (!(rtm.hdr.rtm_flags & RTF_GATEWAY)) { lla = ipv6_linklocal(rt->iface); if (lla == NULL) /* unlikely as we need a LL to get here */ return -1; ADDADDRS(&lla->addr, rt->iface->index); } else { lla = NULL; ADDADDRS(&rt->gate, rt->iface->index); } if (rtm.hdr.rtm_addrs & RTA_NETMASK) { if (rtm.hdr.rtm_flags & RTF_GATEWAY) { memset(&su, 0, sizeof(su)); su.sin.sin6_family = AF_INET6; ADDSU; } else ADDADDR(&rt->net); } if (rtm.hdr.rtm_addrs & RTA_IFP) { /* Make us a link layer socket for the host gateway */ memset(&su, 0, sizeof(su)); su.sdl.sdl_len = sizeof(struct sockaddr_dl); link_addr(rt->iface->name, &su.sdl); ADDSU; } if (rtm.hdr.rtm_addrs & RTA_IFA) { if (lla == NULL) { lla = ipv6_linklocal(rt->iface); if (lla == NULL) /* unlikely */ return -1; } ADDADDRS(&lla->addr, rt->iface->index); } #undef ADDADDR #undef ADDSU #undef SCOPE if (action >= 0 && rt->mtu) { rtm.hdr.rtm_inits |= RTV_MTU; rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu; } rtm.hdr.rtm_msglen = l = bp - (char *)&rtm; if (write(r_fd, &rtm, l) == -1) retval = -1; return retval; }
int if_route6(const struct rt6 *rt, int action) { union sockunion { struct sockaddr sa; struct sockaddr_in6 sin; struct sockaddr_dl sdl; struct sockaddr_storage ss; } su; struct rtm { struct rt_msghdr hdr; char buffer[sizeof(su) * 5]; } rtm; char *bp = rtm.buffer; size_t l; int s, retval; const struct ipv6_addr *lla; if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) return -1; #define ADDSU { \ l = RT_ROUNDUP(su.sa.sa_len); \ memcpy(bp, &su, l); \ bp += l; \ } #define ADDADDRS(addr, scope) { \ memset(&su, 0, sizeof(su)); \ su.sin.sin6_family = AF_INET6; \ su.sin.sin6_len = sizeof(su.sin); \ (&su.sin)->sin6_addr = *addr; \ if (scope) \ ifa_scope(&su.sin, scope); \ ADDSU; \ } #define ADDADDR(addr) ADDADDRS(addr, 0) memset(&rtm, 0, sizeof(rtm)); rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_seq = 1; if (action == 0) rtm.hdr.rtm_type = RTM_CHANGE; else if (action > 0) rtm.hdr.rtm_type = RTM_ADD; else rtm.hdr.rtm_type = RTM_DELETE; rtm.hdr.rtm_flags = RTF_UP | (int)rt->flags; #ifdef RTF_PINNED if (rtm.hdr.rtm_type != RTM_ADD) rtm.hdr.rtm_flags |= RTF_PINNED; #endif rtm.hdr.rtm_addrs = RTA_DST | RTA_NETMASK; #ifdef SIOCGIFPRIORITY rtm.hdr.rtm_priority = rt->metric; #endif /* None interface subnet routes are static. */ if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) { #ifdef RTF_CLONING rtm.hdr.rtm_flags |= RTF_CLONING; #endif } else rtm.hdr.rtm_flags |= RTF_GATEWAY | RTF_STATIC; if (action >= 0) { rtm.hdr.rtm_addrs |= RTA_GATEWAY; if (!(rtm.hdr.rtm_flags & RTF_REJECT)) rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA; } ADDADDR(&rt->dest); lla = NULL; if (rtm.hdr.rtm_addrs & RTA_GATEWAY) { if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) { if_linkaddr(&su.sdl, rt->iface); ADDSU; } else { ADDADDRS(&rt->gate, rt->iface->index); } } if (rtm.hdr.rtm_addrs & RTA_NETMASK) ADDADDR(&rt->net); if (rtm.hdr.rtm_addrs & RTA_IFP) { if_linkaddr(&su.sdl, rt->iface); ADDSU; } if (rtm.hdr.rtm_addrs & RTA_IFA) { if (lla == NULL) { lla = ipv6_linklocal(rt->iface); if (lla == NULL) /* unlikely */ return -1; } ADDADDRS(&lla->addr, rt->iface->index); } #undef ADDADDR #undef ADDSU if (action >= 0 && rt->mtu) { rtm.hdr.rtm_inits |= RTV_MTU; rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu; } rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm); retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0; close(s); return retval; }