#include "defs.h"
#include "tables.h"
#include <time.h>
#include <assert.h>
struct phyint *phyints = NULL;
int num_of_phyints = 0;
static void phyint_print(struct phyint *pi);
static void phyint_insert(struct phyint *pi);
static boolean_t tmptoken_isvalid(struct in6_addr *token);
static void prefix_print(struct prefix *pr);
static void prefix_insert(struct phyint *pi, struct prefix *pr);
static char *prefix_print_state(int state, char *buf, int buflen);
static void prefix_set(struct in6_addr *prefix, struct in6_addr addr,
int bits);
static void adv_prefix_print(struct adv_prefix *adv_pr);
static void adv_prefix_insert(struct phyint *pi, struct adv_prefix *adv_pr);
static void adv_prefix_delete(struct adv_prefix *adv_pr);
static void router_print(struct router *dr);
static void router_insert(struct phyint *pi, struct router *dr);
static void router_delete(struct router *dr);
static void router_add_k(struct router *dr);
static void router_delete_k(struct router *dr);
static int rtmseq;
#define NDP_PREFIX_DEFAULT_LIFETIME (7*24*60*60*1000)
struct phyint *
phyint_lookup(char *name)
{
struct phyint *pi;
if (debug & D_PHYINT)
logmsg(LOG_DEBUG, "phyint_lookup(%s)\n", name);
for (pi = phyints; pi != NULL; pi = pi->pi_next) {
if (strcmp(pi->pi_name, name) == 0)
break;
}
return (pi);
}
struct phyint *
phyint_lookup_on_index(uint_t ifindex)
{
struct phyint *pi;
if (debug & D_PHYINT)
logmsg(LOG_DEBUG, "phyint_lookup_on_index(%d)\n", ifindex);
for (pi = phyints; pi != NULL; pi = pi->pi_next) {
if (pi->pi_index == ifindex)
break;
}
return (pi);
}
struct phyint *
phyint_create(char *name)
{
struct phyint *pi;
int i;
if (debug & D_PHYINT)
logmsg(LOG_DEBUG, "phyint_create(%s)\n", name);
pi = (struct phyint *)calloc(sizeof (struct phyint), 1);
if (pi == NULL) {
logmsg(LOG_ERR, "phyint_create: out of memory\n");
return (NULL);
}
(void) strncpy(pi->pi_name, name, sizeof (pi->pi_name));
pi->pi_name[sizeof (pi->pi_name) - 1] = '\0';
for (i = 0; i < I_IFSIZE; i++)
pi->pi_config[i].cf_value = ifdefaults[i].cf_value;
if (pi->pi_TmpMaxDesyncFactor != 0) {
time_t seed = time(NULL);
srand((uint_t)seed);
pi->pi_TmpDesyncFactor = rand() % pi->pi_TmpMaxDesyncFactor;
pi->pi_TmpDesyncFactor++;
}
pi->pi_TmpRegenCountdown = TIMER_INFINITY;
pi->pi_sock = -1;
pi->pi_stateless = pi->pi_StatelessAddrConf;
pi->pi_stateful = pi->pi_StatefulAddrConf;
pi->pi_autoconf = _B_TRUE;
pi->pi_default_token = _B_TRUE;
if (phyint_init_from_k(pi) == -1) {
free(pi);
return (NULL);
}
phyint_insert(pi);
if (pi->pi_sock != -1) {
if (poll_add(pi->pi_sock) == -1) {
phyint_delete(pi);
return (NULL);
}
}
return (pi);
}
static void
phyint_insert(struct phyint *pi)
{
pi->pi_next = phyints;
pi->pi_prev = NULL;
if (phyints)
phyints->pi_prev = pi;
phyints = pi;
num_of_phyints++;
}
int
phyint_init_from_k(struct phyint *pi)
{
struct ipv6_mreq v6mcastr;
struct lifreq lifr;
int fd;
int save_errno;
boolean_t newsock;
uint_t ttl;
struct sockaddr_in6 *sin6;
if (debug & D_PHYINT)
logmsg(LOG_DEBUG, "phyint_init_from_k(%s)\n", pi->pi_name);
start_over:
if (pi->pi_sock < 0) {
pi->pi_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (pi->pi_sock < 0) {
logperror_pi(pi, "phyint_init_from_k: socket");
return (-1);
}
newsock = _B_TRUE;
} else {
newsock = _B_FALSE;
}
fd = pi->pi_sock;
(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
if (ioctl(fd, SIOCGLIFINDEX, (char *)&lifr) < 0) {
if (errno == ENXIO) {
if (newsock) {
(void) close(pi->pi_sock);
pi->pi_sock = -1;
}
if (debug & D_PHYINT) {
logmsg(LOG_DEBUG, "phyint_init_from_k(%s): "
"not exist\n", pi->pi_name);
}
return (0);
}
logperror_pi(pi, "phyint_init_from_k: SIOCGLIFINDEX");
goto error;
}
if (!newsock && (pi->pi_index != lifr.lifr_index)) {
phyint_cleanup(pi);
goto start_over;
}
pi->pi_index = lifr.lifr_index;
if (ioctl(fd, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: ioctl (get flags)");
goto error;
}
pi->pi_flags = lifr.lifr_flags;
if (!(pi->pi_flags & IFF_UP) || (pi->pi_flags & IFF_NOLOCAL)) {
if (newsock) {
(void) close(pi->pi_sock);
pi->pi_sock = -1;
}
if (debug & D_PHYINT) {
logmsg(LOG_DEBUG, "phyint_init_from_k(%s): "
"IFF_NOLOCAL or not IFF_UP\n", pi->pi_name);
}
return (0);
}
pi->pi_kernel_state |= PI_PRESENT;
if (ioctl(fd, SIOCGLIFMTU, (caddr_t)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: ioctl (get mtu)");
goto error;
}
pi->pi_mtu = lifr.lifr_mtu;
if (ioctl(fd, SIOCGLIFADDR, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: SIOCGLIFADDR");
goto error;
}
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
pi->pi_ifaddr = sin6->sin6_addr;
if (pi->pi_autoconf && pi->pi_default_token) {
if (ioctl(fd, SIOCGLIFTOKEN, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: SIOCGLIFTOKEN");
goto error;
}
sin6 = (struct sockaddr_in6 *)&lifr.lifr_token;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
logmsg(LOG_ERR, "ignoring interface %s: zero token\n",
pi->pi_name);
goto error;
}
pi->pi_token = sin6->sin6_addr;
pi->pi_token_length = lifr.lifr_addrlen;
}
if (pi->pi_flags & IFF_POINTOPOINT) {
if (ioctl(fd, SIOCGLIFDSTADDR, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: SIOCGLIFDSTADDR");
goto error;
}
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
if (sin6->sin6_family != AF_INET6 ||
IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
pi->pi_dst_token = in6addr_any;
} else {
pi->pi_dst_token = sin6->sin6_addr;
pi->pi_dst_token.s6_addr[0] = 0;
pi->pi_dst_token.s6_addr[1] &= 0x3f;
}
} else {
pi->pi_dst_token = in6addr_any;
}
if (newsock) {
icmp6_filter_t filter;
int on = 1;
pi->pi_LinkMTU = pi->pi_mtu;
pi->pi_CurHopLimit = 0;
pi->pi_BaseReachableTime = ND_REACHABLE_TIME;
phyint_reach_random(pi, _B_FALSE);
pi->pi_RetransTimer = ND_RETRANS_TIMER;
if (setsockopt(fd, IPPROTO_IPV6,
IPV6_BOUND_IF, (char *)&pi->pi_index,
sizeof (pi->pi_index)) < 0) {
logperror_pi(pi, "phyint_init_from_k: setsockopt "
"IPV6_BOUND_IF");
goto error;
}
ttl = IPV6_MAX_HOPS;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
(char *)&ttl, sizeof (ttl)) < 0) {
logperror_pi(pi, "phyint_init_from_k: setsockopt "
"IPV6_UNICAST_HOPS");
goto error;
}
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
(char *)&ttl, sizeof (ttl)) < 0) {
logperror_pi(pi, "phyint_init_from_k: setsockopt "
"IPV6_MULTICAST_HOPS");
goto error;
}
v6mcastr.ipv6mr_multiaddr = all_nodes_mcast;
v6mcastr.ipv6mr_interface = pi->pi_index;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
save_errno = errno;
(void) strlcpy(lifr.lifr_name, pi->pi_name, LIFNAMSIZ);
if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1 ||
lifr.lifr_groupname[0] == '\0') {
errno = save_errno;
logperror_pi(pi, "phyint_init_from_k: "
"setsockopt IPV6_JOIN_GROUP");
}
goto error;
}
pi->pi_state |= PI_JOINED_ALLNODES;
pi->pi_kernel_state |= PI_JOINED_ALLNODES;
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,
(char *)&filter, sizeof (filter)) < 0) {
logperror_pi(pi, "phyint_init_from_k: setsockopt "
"ICMP6_FILTER");
goto error;
}
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
(char *)&on, sizeof (on)) < 0) {
logperror_pi(pi, "phyint_init_from_k: setsockopt "
"IPV6_RECVHOPLIMIT");
goto error;
}
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVRTHDR,
(char *)&on, sizeof (on)) < 0) {
logperror_pi(pi, "phyint_init_from_k: setsockopt "
"IPV6_RECVRTHDR");
goto error;
}
}
if (pi->pi_AdvSendAdvertisements &&
!(pi->pi_kernel_state & PI_JOINED_ALLROUTERS)) {
v6mcastr.ipv6mr_multiaddr = all_routers_mcast;
v6mcastr.ipv6mr_interface = pi->pi_index;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
save_errno = errno;
(void) strlcpy(lifr.lifr_name, pi->pi_name, LIFNAMSIZ);
if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1 ||
lifr.lifr_groupname[0] == '\0') {
errno = save_errno;
logperror_pi(pi, "phyint_init_from_k: "
"setsockopt IPV6_JOIN_GROUP");
}
goto error;
}
pi->pi_state |= PI_JOINED_ALLROUTERS;
pi->pi_kernel_state |= PI_JOINED_ALLROUTERS;
}
(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
if (ioctl(fd, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: SIOCGLIFFLAGS");
goto error;
}
if (!(lifr.lifr_flags & IFF_ROUTER) && pi->pi_AdvSendAdvertisements) {
lifr.lifr_flags |= IFF_ROUTER;
if (ioctl(fd, SIOCSLIFFLAGS, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: SIOCSLIFFLAGS");
goto error;
}
pi->pi_flags = lifr.lifr_flags;
}
(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
lifr.lifr_ifinfo.lir_maxmtu = 0;
if (ioctl(fd, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
logperror_pi(pi, "phyint_init_from_k: SIOCSLIFLNKINFO");
goto error;
}
if (debug & D_PHYINT) {
logmsg(LOG_DEBUG, "phyint_init_from_k(%s): done\n",
pi->pi_name);
}
return (0);
error:
pi->pi_kernel_state &= ~PI_PRESENT;
if (newsock) {
(void) close(pi->pi_sock);
pi->pi_sock = -1;
}
return (-1);
}
void
phyint_delete(struct phyint *pi)
{
if (debug & D_PHYINT)
logmsg(LOG_DEBUG, "phyint_delete(%s)\n", pi->pi_name);
assert(num_of_phyints > 0);
while (pi->pi_router_list)
router_delete(pi->pi_router_list);
while (pi->pi_prefix_list) {
prefix_update_ipadm_addrobj(pi->pi_prefix_list, _B_FALSE);
prefix_delete(pi->pi_prefix_list);
}
while (pi->pi_adv_prefix_list)
adv_prefix_delete(pi->pi_adv_prefix_list);
if (pi->pi_sock != -1) {
(void) poll_remove(pi->pi_sock);
if (close(pi->pi_sock) < 0) {
logperror_pi(pi, "phyint_delete: close");
}
pi->pi_sock = -1;
}
if (pi->pi_prev == NULL) {
if (phyints == pi)
phyints = pi->pi_next;
} else {
pi->pi_prev->pi_next = pi->pi_next;
}
if (pi->pi_next != NULL)
pi->pi_next->pi_prev = pi->pi_prev;
pi->pi_next = pi->pi_prev = NULL;
free(pi);
num_of_phyints--;
}
uint_t
phyint_timer(struct phyint *pi, uint_t elapsed)
{
uint_t next = TIMER_INFINITY;
if (pi->pi_AdvSendAdvertisements) {
if (pi->pi_adv_state != NO_ADV) {
int old_state = pi->pi_adv_state;
if (debug & (D_STATE|D_PHYINT)) {
logmsg(LOG_DEBUG, "phyint_timer ADV(%s) "
"state %d\n", pi->pi_name, (int)old_state);
}
next = advertise_event(pi, ADV_TIMER, elapsed);
if (debug & D_STATE) {
logmsg(LOG_DEBUG, "phyint_timer ADV(%s) "
"state %d -> %d\n",
pi->pi_name, (int)old_state,
(int)pi->pi_adv_state);
}
}
} else {
if (pi->pi_sol_state != NO_SOLICIT) {
int old_state = pi->pi_sol_state;
if (debug & (D_STATE|D_PHYINT)) {
logmsg(LOG_DEBUG, "phyint_timer SOL(%s) "
"state %d\n", pi->pi_name, (int)old_state);
}
next = solicit_event(pi, SOL_TIMER, elapsed);
if (debug & D_STATE) {
logmsg(LOG_DEBUG, "phyint_timer SOL(%s) "
"state %d -> %d\n",
pi->pi_name, (int)old_state,
(int)pi->pi_sol_state);
}
}
}
if ((pi->pi_AdvSendAdvertisements && (pi->pi_adv_state != NO_ADV)) ||
(!pi->pi_AdvSendAdvertisements &&
(pi->pi_sol_state != NO_SOLICIT))) {
pi->pi_reach_time_since_random += elapsed;
if (pi->pi_reach_time_since_random >= MAX_REACH_RANDOM_INTERVAL)
phyint_reach_random(pi, _B_TRUE);
}
return (next);
}
static void
phyint_print(struct phyint *pi)
{
struct prefix *pr;
struct adv_prefix *adv_pr;
struct router *dr;
char abuf[INET6_ADDRSTRLEN];
logmsg(LOG_DEBUG, "Phyint %s index %d state %x, kernel %x, "
"num routers %d\n",
pi->pi_name, pi->pi_index, pi->pi_state, pi->pi_kernel_state,
pi->pi_num_k_routers);
logmsg(LOG_DEBUG, "\taddress: %s flags %llx\n",
inet_ntop(AF_INET6, (void *)&pi->pi_ifaddr,
abuf, sizeof (abuf)), pi->pi_flags);
logmsg(LOG_DEBUG, "\tsock %d mtu %d\n", pi->pi_sock, pi->pi_mtu);
logmsg(LOG_DEBUG, "\ttoken: len %d %s\n", pi->pi_token_length,
inet_ntop(AF_INET6, (void *)&pi->pi_token,
abuf, sizeof (abuf)));
if (pi->pi_TmpAddrsEnabled) {
logmsg(LOG_DEBUG, "\ttmp_token: %s\n",
inet_ntop(AF_INET6, (void *)&pi->pi_tmp_token,
abuf, sizeof (abuf)));
logmsg(LOG_DEBUG, "\ttmp config: pref %d valid %d "
"maxdesync %d desync %d regen %d\n",
pi->pi_TmpPreferredLifetime, pi->pi_TmpValidLifetime,
pi->pi_TmpMaxDesyncFactor, pi->pi_TmpDesyncFactor,
pi->pi_TmpRegenAdvance);
}
if (pi->pi_flags & IFF_POINTOPOINT) {
logmsg(LOG_DEBUG, "\tdst_token: %s\n",
inet_ntop(AF_INET6, (void *)&pi->pi_dst_token,
abuf, sizeof (abuf)));
}
logmsg(LOG_DEBUG, "\tLinkMTU %d CurHopLimit %d "
"BaseReachableTime %d\n\tReachableTime %d RetransTimer %d\n",
pi->pi_LinkMTU, pi->pi_CurHopLimit, pi->pi_BaseReachableTime,
pi->pi_ReachableTime, pi->pi_RetransTimer);
if (!pi->pi_AdvSendAdvertisements) {
logmsg(LOG_DEBUG, "\tSOLICIT: time_left %d state %d count %d\n",
pi->pi_sol_time_left, pi->pi_sol_state, pi->pi_sol_count);
} else {
logmsg(LOG_DEBUG, "\tADVERT: time_left %d state %d count %d "
"since last %d\n",
pi->pi_adv_time_left, pi->pi_adv_state, pi->pi_adv_count,
pi->pi_adv_time_since_sent);
print_iflist(pi->pi_config);
}
for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next)
prefix_print(pr);
for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
adv_pr = adv_pr->adv_pr_next) {
adv_prefix_print(adv_pr);
}
for (dr = pi->pi_router_list; dr != NULL; dr = dr->dr_next)
router_print(dr);
logmsg(LOG_DEBUG, "\n");
}
int
phyint_get_lla(struct phyint *pi, struct lifreq *lifrp)
{
struct sockaddr_in6 *sin6;
if (!(pi->pi_flags & IFF_MULTICAST) ||
(pi->pi_flags & IFF_POINTOPOINT)) {
return (-1);
}
(void) strlcpy(lifrp->lifr_name, pi->pi_name, LIFNAMSIZ);
sin6 = (struct sockaddr_in6 *)&(lifrp->lifr_nd.lnr_addr);
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = pi->pi_ifaddr;
if (ioctl(pi->pi_sock, SIOCLIFGETND, lifrp) < 0) {
if (!(pi->pi_flags & IFF_IPMP) || errno != ESRCH)
logperror_pi(pi, "phyint_get_lla: SIOCLIFGETND");
return (-1);
}
return (0);
}
void
phyint_reach_random(struct phyint *pi, boolean_t set_needed)
{
struct lifreq lifr;
pi->pi_ReachableTime = GET_RANDOM(
(int)(ND_MIN_RANDOM_FACTOR * pi->pi_BaseReachableTime),
(int)(ND_MAX_RANDOM_FACTOR * pi->pi_BaseReachableTime));
if (set_needed) {
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, pi->pi_name, LIFNAMSIZ);
lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
logperror_pi(pi,
"phyint_reach_random: SIOCSLIFLNKINFO");
return;
}
}
pi->pi_reach_time_since_random = 0;
}
static boolean_t
tmptoken_isvalid(struct in6_addr *token)
{
struct phyint *pi;
struct in6_addr mask;
struct in6_addr isatap = { 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0x5e, 0xfe, 0, 0, 0, 0 };
struct in6_addr anycast = { 0, 0, 0, 0, \
0, 0, 0, 0, \
0xfd, 0xff, 0xff, 0xff, \
0xff, 0xff, 0xff, 0x80 };
if (IN6_IS_ADDR_UNSPECIFIED(token))
return (_B_FALSE);
if (token->s6_addr[8] & 0x2)
return (_B_FALSE);
(void) memcpy(&mask, token, sizeof (mask));
mask._S6_un._S6_u32[3] = 0;
if (IN6_ARE_ADDR_EQUAL(&isatap, token))
return (_B_FALSE);
mask._S6_un._S6_u32[3] = token->_S6_un._S6_u32[3] & 0xffffff80;
if (IN6_ARE_ADDR_EQUAL(&anycast, token))
return (_B_FALSE);
for (pi = phyints; pi != NULL; pi = pi->pi_next) {
if (((pi->pi_token_length == TMP_TOKEN_BITS) &&
IN6_ARE_ADDR_EQUAL(&pi->pi_token, token)) ||
IN6_ARE_ADDR_EQUAL(&pi->pi_tmp_token, token))
return (_B_FALSE);
}
return (_B_TRUE);
}
boolean_t
tmptoken_create(struct phyint *pi)
{
int fd, i = 0, max_tries = 15;
struct in6_addr token;
uint32_t *tokenp = &(token._S6_un._S6_u32[2]);
char buf[INET6_ADDRSTRLEN];
if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
perror("open /dev/urandom");
goto no_token;
}
bzero((char *)&token, sizeof (token));
do {
if (read(fd, (void *)tokenp, TMP_TOKEN_BYTES) == -1) {
perror("read /dev/urandom");
(void) close(fd);
goto no_token;
}
token.s6_addr[8] &= 0xfd;
i++;
} while (!tmptoken_isvalid(&token) && i < max_tries);
(void) close(fd);
if (i == max_tries) {
no_token:
logmsg(LOG_WARNING, "tmptoken_create(%s): failed to create "
"token; disabling temporary addresses on %s\n",
pi->pi_name, pi->pi_name);
pi->pi_TmpAddrsEnabled = 0;
return (_B_FALSE);
}
pi->pi_tmp_token = token;
if (debug & D_TMP)
logmsg(LOG_DEBUG, "tmptoken_create(%s): created temporary "
"token %s\n", pi->pi_name,
inet_ntop(AF_INET6, &pi->pi_tmp_token, buf, sizeof (buf)));
pi->pi_TmpRegenCountdown = (pi->pi_TmpPreferredLifetime -
pi->pi_TmpDesyncFactor - pi->pi_TmpRegenAdvance) * MILLISEC;
if (pi->pi_TmpRegenCountdown != 0)
timer_schedule(pi->pi_TmpRegenCountdown);
return (_B_TRUE);
}
void
tmptoken_delete(struct phyint *pi)
{
struct prefix *pr;
for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
if (!(pr->pr_flags & IFF_TEMPORARY) ||
(pr->pr_flags & IFF_DEPRECATED) ||
(!token_equal(pr->pr_address, pi->pi_tmp_token,
TMP_TOKEN_BITS))) {
continue;
}
pr->pr_PreferredLifetime = 0;
pr->pr_state |= PR_DEPRECATED;
prefix_update_k(pr);
}
(void) memset(&pi->pi_tmp_token, 0, sizeof (pi->pi_tmp_token));
}
uint_t
tmptoken_timer(struct phyint *pi, uint_t elapsed)
{
struct nd_opt_prefix_info opt;
struct sockaddr_in6 sin6;
struct prefix *pr, *newpr;
if (debug & D_TMP) {
logmsg(LOG_DEBUG, "tmptoken_timer(%s, %d) regencountdown %d\n",
pi->pi_name, (int)elapsed, pi->pi_TmpRegenCountdown);
}
if (!pi->pi_TmpAddrsEnabled ||
(pi->pi_TmpRegenCountdown == TIMER_INFINITY))
return (TIMER_INFINITY);
if (pi->pi_TmpRegenCountdown > elapsed) {
pi->pi_TmpRegenCountdown -= elapsed;
return (pi->pi_TmpRegenCountdown);
}
if (!tmptoken_create(pi))
return (TIMER_INFINITY);
for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
if (!(pr->pr_state & PR_AUTO) || pr->pr_state & PR_STATIC ||
pr->pr_state & PR_DEPRECATED ||
pr->pr_flags & IFF_TEMPORARY)
continue;
newpr = prefix_create(pi, pr->pr_prefix, pr->pr_prefix_len,
IFF_TEMPORARY);
if (newpr == NULL) {
char pbuf[INET6_ADDRSTRLEN];
char tbuf[INET6_ADDRSTRLEN];
(void) inet_ntop(AF_INET6, &pr->pr_prefix, pbuf,
sizeof (pbuf));
(void) inet_ntop(AF_INET6, &pi->pi_tmp_token, tbuf,
sizeof (tbuf));
logmsg(LOG_ERR, "can't create new tmp addr "
"(%s, %s, %s)\n", pi->pi_name, pbuf, tbuf);
continue;
}
opt.nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
opt.nd_opt_pi_len = sizeof (opt) / 8;
opt.nd_opt_pi_prefix_len = pr->pr_prefix_len;
opt.nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_AUTO;
opt.nd_opt_pi_valid_time =
htonl(pr->pr_ValidLifetime / 1000);
opt.nd_opt_pi_preferred_time =
htonl(pr->pr_PreferredLifetime / 1000);
if (pr->pr_state & PR_ONLINK)
opt.nd_opt_pi_flags_reserved &= ND_OPT_PI_FLAG_ONLINK;
opt.nd_opt_pi_prefix = pr->pr_prefix;
(void) memset(&sin6, 0, sizeof (sin6));
if (!incoming_prefix_addrconf_process(pi, newpr,
(uchar_t *)&opt, &sin6, _B_FALSE, _B_TRUE)) {
char pbuf[INET6_ADDRSTRLEN];
char tbuf[INET6_ADDRSTRLEN];
(void) inet_ntop(AF_INET6, &pr->pr_prefix, pbuf,
sizeof (pbuf));
(void) inet_ntop(AF_INET6, &pi->pi_tmp_token, tbuf,
sizeof (tbuf));
logmsg(LOG_ERR, "can't create new tmp addr "
"(%s, %s, %s)\n", pi->pi_name, pbuf, tbuf);
continue;
}
if (pr->pr_state & PR_ONLINK) {
incoming_prefix_onlink_process(newpr, (uchar_t *)&opt);
}
}
return (TIMER_INFINITY);
}
boolean_t
token_equal(struct in6_addr t1, struct in6_addr t2, int tlen)
{
uchar_t mask;
int j, abytes, tbytes, tbits;
if (tlen < 0 || tlen > IPV6_ABITS)
return (_B_FALSE);
abytes = IPV6_ABITS >> 3;
tbytes = tlen >> 3;
tbits = tlen & 7;
for (j = abytes - 1; j >= abytes - tbytes; j--)
if (t1.s6_addr[j] != t2.s6_addr[j])
return (_B_FALSE);
if (tbits == 0)
return (_B_TRUE);
mask = 0xff >> (8 - tbits);
if ((t1.s6_addr[j] & mask) != (t2.s6_addr[j] & mask))
return (_B_FALSE);
return (_B_TRUE);
}
static struct prefix *
prefix_lookup(struct phyint *pi, struct in6_addr prefix, int prefixlen)
{
struct prefix *pr;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_lookup(%s, %s/%u)\n", pi->pi_name,
inet_ntop(AF_INET6, (void *)&prefix,
abuf, sizeof (abuf)), prefixlen);
}
for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
if (pr->pr_prefix_len == prefixlen &&
prefix_equal(prefix, pr->pr_prefix, prefixlen))
return (pr);
}
return (NULL);
}
boolean_t
prefix_equal(struct in6_addr p1, struct in6_addr p2, int plen)
{
uchar_t mask;
int j, pbytes, pbits;
if (plen < 0 || plen > IPV6_ABITS)
return (_B_FALSE);
pbytes = plen >> 3;
pbits = plen & 7;
for (j = 0; j < pbytes; j++)
if (p1.s6_addr[j] != p2.s6_addr[j])
return (_B_FALSE);
if (pbits == 0)
return (_B_TRUE);
mask = 0xff << (8 - pbits);
if ((p1.s6_addr[j] & mask) != (p2.s6_addr[j] & mask))
return (_B_FALSE);
return (_B_TRUE);
}
void
prefix_set(struct in6_addr *prefix, struct in6_addr addr, int prefix_len)
{
uchar_t mask;
int j;
if (prefix_len < 0 || prefix_len > IPV6_ABITS)
return;
bzero((char *)prefix, sizeof (*prefix));
for (j = 0; prefix_len > 8; prefix_len -= 8, j++)
prefix->s6_addr[j] = addr.s6_addr[j];
mask = 0xff << (8 - prefix_len);
prefix->s6_addr[j] = addr.s6_addr[j] & mask;
}
struct prefix *
prefix_lookup_name(struct phyint *pi, char *name)
{
struct prefix *pr;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_lookup_name(%s, %s)\n",
pi->pi_name, name);
}
if (name[0] == '\0')
return (NULL);
for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
if (strcmp(name, pr->pr_name) == 0)
return (pr);
}
return (NULL);
}
struct prefix *
prefix_lookup_addr_match(struct prefix *pr)
{
char abuf[INET6_ADDRSTRLEN];
struct phyint *pi;
struct prefix *otherpr = NULL;
struct in6_addr prefix;
int prefixlen;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_lookup_addr_match(%s/%u)\n",
inet_ntop(AF_INET6, (void *)&pr->pr_address,
abuf, sizeof (abuf)), pr->pr_prefix_len);
}
prefix = pr->pr_prefix;
prefixlen = pr->pr_prefix_len;
for (pi = phyints; pi != NULL; pi = pi->pi_next) {
otherpr = prefix_lookup(pi, prefix, prefixlen);
if (otherpr == pr)
continue;
if (otherpr != NULL && (otherpr->pr_state & PR_AUTO) &&
IN6_ARE_ADDR_EQUAL(&pr->pr_address,
&otherpr->pr_address))
return (otherpr);
}
return (NULL);
}
struct prefix *
prefix_create(struct phyint *pi, struct in6_addr prefix, int prefixlen,
uint64_t flags)
{
struct prefix *pr;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_create(%s, %s/%u, 0x%llx)\n",
pi->pi_name, inet_ntop(AF_INET6, (void *)&prefix,
abuf, sizeof (abuf)), prefixlen, flags);
}
pr = (struct prefix *)calloc(sizeof (struct prefix), 1);
if (pr == NULL) {
logmsg(LOG_ERR, "prefix_create: out of memory\n");
return (NULL);
}
prefix_set(&pr->pr_prefix, prefix, prefixlen);
pr->pr_prefix_len = prefixlen;
pr->pr_PreferredLifetime = PREFIX_INFINITY;
pr->pr_ValidLifetime = PREFIX_INFINITY;
pr->pr_OnLinkLifetime = PREFIX_INFINITY;
pr->pr_kernel_state = 0;
pr->pr_flags |= flags;
prefix_insert(pi, pr);
return (pr);
}
struct prefix *
prefix_create_name(struct phyint *pi, char *name)
{
struct prefix *pr;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_create_name(%s, %s)\n",
pi->pi_name, name);
}
pr = (struct prefix *)calloc(sizeof (struct prefix), 1);
if (pr == NULL) {
logmsg(LOG_ERR, "prefix_create_name: out of memory\n");
return (NULL);
}
(void) strncpy(pr->pr_name, name, sizeof (pr->pr_name));
pr->pr_name[sizeof (pr->pr_name) - 1] = '\0';
prefix_insert(pi, pr);
return (pr);
}
static void
prefix_insert(struct phyint *pi, struct prefix *pr)
{
pr->pr_next = pi->pi_prefix_list;
pr->pr_prev = NULL;
if (pi->pi_prefix_list != NULL)
pi->pi_prefix_list->pr_prev = pr;
pi->pi_prefix_list = pr;
pr->pr_physical = pi;
}
int
prefix_init_from_k(struct prefix *pr)
{
struct lifreq lifr;
struct sockaddr_in6 *sin6;
int sock = pr->pr_physical->pi_sock;
(void) strncpy(lifr.lifr_name, pr->pr_name, sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
if (ioctl(sock, SIOCGLIFADDR, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_init_from_k: ioctl (get addr)");
goto error;
}
if (lifr.lifr_addr.ss_family != AF_INET6) {
logmsg(LOG_ERR, "ignoring interface %s: not AF_INET6\n",
pr->pr_name);
goto error;
}
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
pr->pr_address = sin6->sin6_addr;
if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_init_from_k: ioctl (get flags)");
goto error;
}
pr->pr_flags = lifr.lifr_flags;
if (lifr.lifr_flags & IFF_DHCPRUNNING) {
struct phyint *pi = pr->pr_physical;
struct prefix *pr2;
pr->pr_prefix_len = IPV6_ABITS;
if (!(lifr.lifr_flags & IFF_UP) ||
IN6_IS_ADDR_UNSPECIFIED(&pr->pr_address) ||
IN6_IS_ADDR_LINKLOCAL(&pr->pr_address)) {
if (debug & D_DHCP)
logmsg(LOG_DEBUG, "prefix_init_from_k: "
"ignoring DHCP %s not ready\n",
pr->pr_name);
return (0);
}
for (pr2 = pi->pi_prefix_list; pr2 != NULL;
pr2 = pr2->pr_next) {
if (pr2->pr_prefix_len != IPV6_ABITS &&
(!(pr2->pr_state & PR_STATIC) ||
(pr2->pr_flags & IFF_DHCPRUNNING)) &&
prefix_equal(pr->pr_prefix, pr2->pr_prefix,
pr2->pr_prefix_len)) {
pr->pr_prefix_len = pr2->pr_prefix_len;
break;
}
}
if (pr2 == NULL) {
if (debug & D_DHCP)
logmsg(LOG_DEBUG, "prefix_init_from_k: no "
"saved mask for DHCP %s; need to "
"resolicit\n", pr->pr_name);
(void) check_to_solicit(pi, RESTART_INIT_SOLICIT);
} else {
if (debug & D_DHCP)
logmsg(LOG_DEBUG, "prefix_init_from_k: using "
"%s mask for DHCP %s\n",
pr2->pr_name[0] == '\0' ? "saved" :
pr2->pr_name, pr->pr_name);
prefix_update_dhcp(pr);
}
prefix_update_ipadm_addrobj(pr, _B_TRUE);
} else {
if (ioctl(sock, SIOCGLIFSUBNET, (char *)&lifr) < 0) {
logperror_pr(pr,
"prefix_init_from_k: ioctl (get subnet)");
goto error;
}
if (lifr.lifr_subnet.ss_family != AF_INET6) {
logmsg(LOG_ERR,
"ignoring interface %s: not AF_INET6\n",
pr->pr_name);
goto error;
}
sin6 = (struct sockaddr_in6 *)&lifr.lifr_subnet;
pr->pr_prefix_len = lifr.lifr_addrlen;
prefix_set(&pr->pr_prefix, sin6->sin6_addr, pr->pr_prefix_len);
if (pr->pr_prefix_len != IPV6_ABITS &&
(pr->pr_flags & IFF_UP) &&
IN6_ARE_ADDR_EQUAL(&pr->pr_address, &pr->pr_prefix)) {
char abuf[INET6_ADDRSTRLEN];
logmsg(LOG_ERR, "ignoring interface %s: it appears to "
"be configured with an invalid interface id "
"(%s/%u)\n",
pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_address,
abuf, sizeof (abuf)), pr->pr_prefix_len);
goto error;
}
}
pr->pr_kernel_state = 0;
if (pr->pr_prefix_len != IPV6_ABITS)
pr->pr_kernel_state |= PR_ONLINK;
if (!(pr->pr_flags & (IFF_NOLOCAL | IFF_DHCPRUNNING)))
pr->pr_kernel_state |= PR_AUTO;
if ((pr->pr_flags & IFF_DEPRECATED) && (pr->pr_kernel_state & PR_AUTO))
pr->pr_kernel_state |= PR_DEPRECATED;
if (!(pr->pr_flags & IFF_ADDRCONF)) {
pr->pr_kernel_state |= PR_STATIC;
}
pr->pr_state = pr->pr_kernel_state;
if (pr->pr_state & PR_AUTO) {
pr->pr_prefix_len =
IPV6_ABITS - pr->pr_physical->pi_token_length;
prefix_set(&pr->pr_prefix, pr->pr_prefix, pr->pr_prefix_len);
}
pr->pr_ValidLifetime = NDP_PREFIX_DEFAULT_LIFETIME;
pr->pr_PreferredLifetime = NDP_PREFIX_DEFAULT_LIFETIME;
pr->pr_OnLinkLifetime = NDP_PREFIX_DEFAULT_LIFETIME;
if (pr->pr_flags & IFF_TEMPORARY)
pr->pr_CreateTime = getcurrenttime() / MILLISEC;
if (pr->pr_kernel_state == 0)
pr->pr_name[0] = '\0';
return (0);
error:
pr->pr_kernel_state = 0;
pr->pr_name[0] = '\0';
return (-1);
}
void
prefix_delete(struct prefix *pr)
{
struct phyint *pi;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_delete(%s, %s, %s/%u)\n",
pr->pr_physical->pi_name, pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
abuf, sizeof (abuf)), pr->pr_prefix_len);
}
pi = pr->pr_physical;
pr->pr_state &= PR_STATIC;
if (pr->pr_kernel_state != pr->pr_state)
prefix_update_k(pr);
if (pr->pr_prev == NULL) {
if (pi != NULL)
pi->pi_prefix_list = pr->pr_next;
} else {
pr->pr_prev->pr_next = pr->pr_next;
}
if (pr->pr_next != NULL)
pr->pr_next->pr_prev = pr->pr_prev;
pr->pr_next = pr->pr_prev = NULL;
free(pr);
}
static int
prefix_modify_flags(struct prefix *pr, uint64_t onflags, uint64_t offflags)
{
struct lifreq lifr;
struct phyint *pi = pr->pr_physical;
uint64_t old_flags;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_modify_flags(%s, %s, %s/%u) "
"flags %llx on %llx off %llx\n",
pr->pr_physical->pi_name,
pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
abuf, sizeof (abuf)), pr->pr_prefix_len,
pr->pr_flags, onflags, offflags);
}
if (!(pr->pr_state & PR_STATIC) &&
strcmp(pr->pr_name, pi->pi_name) == 0) {
logmsg(LOG_ERR, "prefix_modify_flags(%s, on %llx, off %llx): "
"name matches interface name\n",
pi->pi_name, onflags, offflags);
return (-1);
}
(void) strncpy(lifr.lifr_name, pr->pr_name, sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
if (ioctl(pi->pi_sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
if (errno != ENXIO) {
logperror_pr(pr, "prefix_modify_flags: SIOCGLIFFLAGS");
logmsg(LOG_ERR, "prefix_modify_flags(%s, %s) old 0x%llx"
" on 0x%llx off 0x%llx\n", pr->pr_physical->pi_name,
pr->pr_name, pr->pr_flags, onflags, offflags);
}
return (-1);
}
old_flags = lifr.lifr_flags;
lifr.lifr_flags |= onflags;
lifr.lifr_flags &= ~offflags;
pr->pr_flags = lifr.lifr_flags;
if (ioctl(pi->pi_sock, SIOCSLIFFLAGS, (char *)&lifr) < 0) {
if (errno != ENXIO) {
logperror_pr(pr, "prefix_modify_flags: SIOCSLIFFLAGS");
logmsg(LOG_ERR, "prefix_modify_flags(%s, %s) old 0x%llx"
" new 0x%llx on 0x%llx off 0x%llx\n",
pr->pr_physical->pi_name, pr->pr_name,
old_flags, lifr.lifr_flags, onflags, offflags);
}
return (-1);
}
return (0);
}
void
prefix_update_dhcp(struct prefix *pr)
{
struct lifreq lifr;
(void) memset(&lifr, 0, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, pr->pr_name, sizeof (lifr.lifr_name));
lifr.lifr_addr.ss_family = AF_INET6;
prefix_set(&((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr,
pr->pr_address, pr->pr_prefix_len);
lifr.lifr_addrlen = pr->pr_prefix_len;
if (ioctl(pr->pr_physical->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) ==
-1 && errno != ENXIO)
logperror_pr(pr, "prefix_update_dhcp: ioctl (set subnet)");
}
void
prefix_update_k(struct prefix *pr)
{
struct lifreq lifr;
char abuf[INET6_ADDRSTRLEN];
char buf1[PREFIX_STATESTRLEN], buf2[PREFIX_STATESTRLEN];
struct phyint *pi = pr->pr_physical;
struct sockaddr_in6 *sin6;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s, %s, %s/%u) "
"from %s to %s\n", pr->pr_physical->pi_name, pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
abuf, sizeof (abuf)), pr->pr_prefix_len,
prefix_print_state(pr->pr_kernel_state, buf1,
sizeof (buf1)),
prefix_print_state(pr->pr_state, buf2, sizeof (buf2)));
}
if (pr->pr_kernel_state == pr->pr_state)
return;
if (pr->pr_state & PR_STATIC)
return;
if (pr->pr_kernel_state == 0) {
uint64_t onflags;
if (pr->pr_name[0] != '\0') {
logmsg(LOG_ERR, "prefix_update_k(%s, %s, %s/%u) "
"from %s to %s name is already allocated\n",
pr->pr_physical->pi_name, pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
abuf, sizeof (abuf)), pr->pr_prefix_len,
prefix_print_state(pr->pr_kernel_state, buf1,
sizeof (buf1)),
prefix_print_state(pr->pr_state, buf2,
sizeof (buf2)));
return;
}
(void) strncpy(lifr.lifr_name, pi->pi_name,
sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
lifr.lifr_addr.ss_family = AF_UNSPEC;
if (ioctl(pi->pi_sock, SIOCLIFADDIF, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_update_k: SIOCLIFADDIF");
return;
}
(void) strncpy(pr->pr_name, lifr.lifr_name,
sizeof (pr->pr_name));
pr->pr_name[sizeof (pr->pr_name) - 1] = '\0';
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k: new name %s\n",
pr->pr_name);
}
onflags = IFF_ADDRCONF;
if (pr->pr_flags & IFF_TEMPORARY)
onflags |= IFF_TEMPORARY;
if (prefix_modify_flags(pr, onflags, 0) == -1)
return;
}
if ((pr->pr_state & (PR_ONLINK|PR_AUTO)) == 0) {
if (prefix_modify_flags(pr, 0, IFF_UP|IFF_DEPRECATED) == -1)
return;
(void) strncpy(lifr.lifr_name, pr->pr_name,
sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k: remove name %s\n",
pr->pr_name);
}
if (!(pr->pr_state & PR_STATIC) &&
strcmp(pr->pr_name, pi->pi_name) == 0) {
logmsg(LOG_ERR, "prefix_update_k(%s): "
"name matches if\n", pi->pi_name);
return;
}
lifr.lifr_addr.ss_family = AF_UNSPEC;
if (ioctl(pi->pi_sock, SIOCLIFREMOVEIF, (char *)&lifr) < 0 &&
errno != ENXIO) {
logperror_pr(pr, "prefix_update_k: SIOCLIFREMOVEIF");
}
pr->pr_kernel_state = 0;
pr->pr_name[0] = '\0';
return;
}
if ((pr->pr_state & PR_AUTO) && !(pr->pr_kernel_state & PR_AUTO)) {
(void) strncpy(lifr.lifr_name, pr->pr_name,
sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
bzero(sin6, sizeof (struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = pr->pr_address;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s) set addr %s "
"for PR_AUTO on\n",
pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_address,
abuf, sizeof (abuf)));
}
if (ioctl(pi->pi_sock, SIOCSLIFADDR, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_update_k: SIOCSLIFADDR");
return;
}
prefix_update_ipadm_addrobj(pr, _B_TRUE);
if (pr->pr_state & PR_ONLINK) {
sin6->sin6_addr = pr->pr_prefix;
lifr.lifr_addrlen = pr->pr_prefix_len;
} else {
sin6->sin6_addr = pr->pr_address;
lifr.lifr_addrlen = IPV6_ABITS;
}
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s) set subnet "
"%s/%u for PR_AUTO on\n", pr->pr_name,
inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
abuf, sizeof (abuf)), lifr.lifr_addrlen);
}
if (ioctl(pi->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_update_k: SIOCSLIFSUBNET");
return;
}
if (pi->pi_flags & IFF_POINTOPOINT) {
int i;
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
bzero(sin6, sizeof (struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = pr->pr_prefix;
for (i = 0; i < 16; i++) {
sin6->sin6_addr.s6_addr[i] |=
pi->pi_dst_token.s6_addr[i];
}
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s) "
"set dstaddr %s for PR_AUTO on\n",
pr->pr_name, inet_ntop(AF_INET6,
(void *)&sin6->sin6_addr,
abuf, sizeof (abuf)));
}
if (ioctl(pi->pi_sock, SIOCSLIFDSTADDR,
(char *)&lifr) < 0) {
logperror_pr(pr,
"prefix_update_k: SIOCSLIFDSTADDR");
return;
}
}
if (prefix_modify_flags(pr, IFF_UP, IFF_NOLOCAL) == -1)
return;
pr->pr_kernel_state |= PR_AUTO;
if (pr->pr_state & PR_ONLINK)
pr->pr_kernel_state |= PR_ONLINK;
else
pr->pr_kernel_state &= ~PR_ONLINK;
}
if (!(pr->pr_state & PR_AUTO) && (pr->pr_kernel_state & PR_AUTO)) {
if (prefix_modify_flags(pr, IFF_NOLOCAL, 0) == -1)
return;
(void) strncpy(lifr.lifr_name, pr->pr_name,
sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
bzero(sin6, sizeof (struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s) set addr %s "
"for PR_AUTO off\n", pr->pr_name,
inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
abuf, sizeof (abuf)));
}
if (ioctl(pi->pi_sock, SIOCSLIFADDR, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_update_k: SIOCSLIFADDR");
return;
}
pr->pr_kernel_state &= ~PR_AUTO;
}
if ((pr->pr_state & PR_DEPRECATED) &&
!(pr->pr_kernel_state & PR_DEPRECATED) &&
(pr->pr_kernel_state & PR_AUTO)) {
if (prefix_modify_flags(pr, IFF_DEPRECATED, 0) == -1)
return;
pr->pr_kernel_state |= PR_DEPRECATED;
}
if (!(pr->pr_state & PR_DEPRECATED) &&
(pr->pr_kernel_state & PR_DEPRECATED)) {
if (prefix_modify_flags(pr, 0, IFF_DEPRECATED) == -1)
return;
pr->pr_kernel_state &= ~PR_DEPRECATED;
}
if ((pr->pr_state & PR_ONLINK) && !(pr->pr_kernel_state & PR_ONLINK)) {
(void) strncpy(lifr.lifr_name, pr->pr_name,
sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
bzero(sin6, sizeof (struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = pr->pr_prefix;
lifr.lifr_addrlen = pr->pr_prefix_len;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s) set subnet "
"%s/%d for PR_ONLINK on\n", pr->pr_name,
inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
abuf, sizeof (abuf)), lifr.lifr_addrlen);
}
if (ioctl(pi->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_update_k: SIOCSLIFSUBNET");
return;
}
if (!(pr->pr_state & PR_AUTO) &&
prefix_modify_flags(pr, IFF_UP | IFF_NOLOCAL, 0) == -1)
return;
pr->pr_kernel_state |= PR_ONLINK;
}
if (!(pr->pr_state & PR_ONLINK) && (pr->pr_kernel_state & PR_ONLINK)) {
(void) strncpy(lifr.lifr_name, pr->pr_name,
sizeof (lifr.lifr_name));
lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
bzero(sin6, sizeof (struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = pr->pr_address;
lifr.lifr_addrlen = IPV6_ABITS;
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "prefix_update_k(%s) set subnet "
"%s/%d for PR_ONLINK off\n", pr->pr_name,
inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
abuf, sizeof (abuf)), lifr.lifr_addrlen);
}
if (ioctl(pi->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) < 0) {
logperror_pr(pr, "prefix_update_k: SIOCSLIFSUBNET");
return;
}
pr->pr_kernel_state &= ~PR_ONLINK;
}
}
uint_t
prefix_timer(struct prefix *pr, uint_t elapsed)
{
uint_t next = TIMER_INFINITY;
char abuf[INET6_ADDRSTRLEN];
if (debug & (D_PREFIX|D_TMP)) {
logmsg(LOG_DEBUG, "prefix_timer(%s, %s/%u, %d) "
"valid %d pref %d onlink %d\n",
pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
abuf, sizeof (abuf)), pr->pr_prefix_len,
elapsed, pr->pr_ValidLifetime, pr->pr_PreferredLifetime,
pr->pr_OnLinkLifetime);
}
if (pr->pr_state & PR_STATIC)
return (next);
if (pr->pr_AutonomousFlag &&
(pr->pr_PreferredLifetime != PREFIX_INFINITY)) {
if (pr->pr_PreferredLifetime <= elapsed) {
pr->pr_PreferredLifetime = 0;
} else {
pr->pr_PreferredLifetime -= elapsed;
if (pr->pr_PreferredLifetime < next)
next = pr->pr_PreferredLifetime;
}
}
if (pr->pr_AutonomousFlag &&
(pr->pr_ValidLifetime != PREFIX_INFINITY)) {
if (pr->pr_ValidLifetime <= elapsed) {
pr->pr_ValidLifetime = 0;
} else {
pr->pr_ValidLifetime -= elapsed;
if (pr->pr_ValidLifetime < next)
next = pr->pr_ValidLifetime;
}
}
if (pr->pr_OnLinkFlag &&
(pr->pr_OnLinkLifetime != PREFIX_INFINITY)) {
if (pr->pr_OnLinkLifetime <= elapsed) {
pr->pr_OnLinkLifetime = 0;
} else {
pr->pr_OnLinkLifetime -= elapsed;
if (pr->pr_OnLinkLifetime < next)
next = pr->pr_OnLinkLifetime;
}
}
if (pr->pr_AutonomousFlag && pr->pr_ValidLifetime == 0)
pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
if (pr->pr_AutonomousFlag && pr->pr_PreferredLifetime == 0 &&
(pr->pr_state & PR_AUTO)) {
pr->pr_state |= PR_DEPRECATED;
if (debug & D_TMP)
logmsg(LOG_WARNING, "prefix_timer: deprecated "
"prefix(%s)\n", pr->pr_name);
}
if (pr->pr_OnLinkFlag && pr->pr_OnLinkLifetime == 0)
pr->pr_state &= ~PR_ONLINK;
if (pr->pr_state != pr->pr_kernel_state) {
if ((pr->pr_kernel_state & PR_AUTO) &&
!(pr->pr_state & PR_AUTO)) {
char abuf[INET6_ADDRSTRLEN];
logmsg(LOG_WARNING,
"Address removed due to timeout %s\n",
inet_ntop(AF_INET6, (void *)&pr->pr_address,
abuf, sizeof (abuf)));
}
prefix_update_k(pr);
}
return (next);
}
static char *
prefix_print_state(int state, char *buf, int buflen)
{
char *cp;
int cplen = buflen;
cp = buf;
cp[0] = '\0';
if (state & PR_ONLINK) {
if (strlcat(cp, "ONLINK ", cplen) >= cplen)
return (buf);
cp += strlen(cp);
cplen = buflen - (cp - buf);
}
if (state & PR_AUTO) {
if (strlcat(cp, "AUTO ", cplen) >= cplen)
return (buf);
cp += strlen(cp);
cplen = buflen - (cp - buf);
}
if (state & PR_DEPRECATED) {
if (strlcat(cp, "DEPRECATED ", cplen) >= cplen)
return (buf);
cp += strlen(cp);
cplen = buflen - (cp - buf);
}
if (state & PR_STATIC) {
if (strlcat(cp, "STATIC ", cplen) >= cplen)
return (buf);
cp += strlen(cp);
cplen = buflen - (cp - buf);
}
return (buf);
}
static void
prefix_print(struct prefix *pr)
{
char abuf[INET6_ADDRSTRLEN];
char buf1[PREFIX_STATESTRLEN], buf2[PREFIX_STATESTRLEN];
logmsg(LOG_DEBUG, "Prefix name: %s prefix %s/%u state %s "
"kernel_state %s\n", pr->pr_name,
inet_ntop(AF_INET6, (void *)&pr->pr_prefix, abuf, sizeof (abuf)),
pr->pr_prefix_len,
prefix_print_state(pr->pr_state, buf2, sizeof (buf2)),
prefix_print_state(pr->pr_kernel_state, buf1, sizeof (buf1)));
logmsg(LOG_DEBUG, "\tAddress: %s flags %llx in_use %d\n",
inet_ntop(AF_INET6, (void *)&pr->pr_address, abuf, sizeof (abuf)),
pr->pr_flags, pr->pr_in_use);
logmsg(LOG_DEBUG, "\tValidLifetime %u PreferredLifetime %u "
"OnLinkLifetime %u\n", pr->pr_ValidLifetime,
pr->pr_PreferredLifetime, pr->pr_OnLinkLifetime);
logmsg(LOG_DEBUG, "\tOnLink %d Auto %d\n",
pr->pr_OnLinkFlag, pr->pr_AutonomousFlag);
logmsg(LOG_DEBUG, "\n");
}
struct adv_prefix *
adv_prefix_lookup(struct phyint *pi, struct in6_addr prefix, int prefixlen)
{
struct adv_prefix *adv_pr;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "adv_prefix_lookup(%s, %s/%u)\n",
pi->pi_name, inet_ntop(AF_INET6, (void *)&prefix,
abuf, sizeof (abuf)), prefixlen);
}
for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
adv_pr = adv_pr->adv_pr_next) {
if (adv_pr->adv_pr_prefix_len == prefixlen &&
prefix_equal(prefix, adv_pr->adv_pr_prefix, prefixlen))
return (adv_pr);
}
return (NULL);
}
struct adv_prefix *
adv_prefix_create(struct phyint *pi, struct in6_addr prefix, int prefixlen)
{
struct adv_prefix *adv_pr;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "adv_prefix_create(%s, %s/%u)\n",
pi->pi_name, inet_ntop(AF_INET6, (void *)&prefix,
abuf, sizeof (abuf)), prefixlen);
}
adv_pr = (struct adv_prefix *)calloc(sizeof (struct adv_prefix), 1);
if (adv_pr == NULL) {
logmsg(LOG_ERR, "adv_prefix_create: calloc\n");
return (NULL);
}
prefix_set(&adv_pr->adv_pr_prefix, prefix, prefixlen);
adv_pr->adv_pr_prefix_len = prefixlen;
adv_prefix_insert(pi, adv_pr);
return (adv_pr);
}
static void
adv_prefix_insert(struct phyint *pi, struct adv_prefix *adv_pr)
{
adv_pr->adv_pr_next = pi->pi_adv_prefix_list;
adv_pr->adv_pr_prev = NULL;
if (pi->pi_adv_prefix_list != NULL)
pi->pi_adv_prefix_list->adv_pr_prev = adv_pr;
pi->pi_adv_prefix_list = adv_pr;
adv_pr->adv_pr_physical = pi;
}
static void
adv_prefix_delete(struct adv_prefix *adv_pr)
{
struct phyint *pi;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "adv_prefix_delete(%s, %s/%u)\n",
adv_pr->adv_pr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
abuf, sizeof (abuf)), adv_pr->adv_pr_prefix_len);
}
pi = adv_pr->adv_pr_physical;
if (adv_pr->adv_pr_prev == NULL) {
if (pi != NULL)
pi->pi_adv_prefix_list = adv_pr->adv_pr_next;
} else {
adv_pr->adv_pr_prev->adv_pr_next = adv_pr->adv_pr_next;
}
if (adv_pr->adv_pr_next != NULL)
adv_pr->adv_pr_next->adv_pr_prev = adv_pr->adv_pr_prev;
adv_pr->adv_pr_next = adv_pr->adv_pr_prev = NULL;
free(adv_pr);
}
uint_t
adv_prefix_timer(struct adv_prefix *adv_pr, uint_t elapsed)
{
int seconds_elapsed = (elapsed + 500) / 1000;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_PREFIX) {
logmsg(LOG_DEBUG, "adv_prefix_timer(%s, %s/%u, %d)\n",
adv_pr->adv_pr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
abuf, sizeof (abuf)), adv_pr->adv_pr_prefix_len,
elapsed);
}
if (adv_pr->adv_pr_AdvValidRealTime) {
if (adv_pr->adv_pr_AdvValidExpiration > seconds_elapsed)
adv_pr->adv_pr_AdvValidExpiration -= seconds_elapsed;
else
adv_pr->adv_pr_AdvValidExpiration = 0;
}
if (adv_pr->adv_pr_AdvPreferredRealTime) {
if (adv_pr->adv_pr_AdvPreferredExpiration > seconds_elapsed) {
adv_pr->adv_pr_AdvPreferredExpiration -=
seconds_elapsed;
} else {
adv_pr->adv_pr_AdvPreferredExpiration = 0;
}
}
return (TIMER_INFINITY);
}
static void
adv_prefix_print(struct adv_prefix *adv_pr)
{
print_prefixlist(adv_pr->adv_pr_config);
}
struct router *
router_lookup(struct phyint *pi, struct in6_addr addr)
{
struct router *dr;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_lookup(%s, %s)\n", pi->pi_name,
inet_ntop(AF_INET6, (void *)&addr,
abuf, sizeof (abuf)));
}
for (dr = pi->pi_router_list; dr != NULL; dr = dr->dr_next) {
if (bcmp((char *)&addr, (char *)&dr->dr_address,
sizeof (addr)) == 0)
return (dr);
}
return (NULL);
}
struct router *
router_create(struct phyint *pi, struct in6_addr addr, uint_t lifetime)
{
struct router *dr;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_create(%s, %s, %u)\n", pi->pi_name,
inet_ntop(AF_INET6, (void *)&addr,
abuf, sizeof (abuf)), lifetime);
}
dr = (struct router *)calloc(sizeof (struct router), 1);
if (dr == NULL) {
logmsg(LOG_ERR, "router_create: out of memory\n");
return (NULL);
}
dr->dr_address = addr;
dr->dr_lifetime = lifetime;
router_insert(pi, dr);
if (dr->dr_lifetime != 0)
router_add_k(dr);
return (dr);
}
static void
router_insert(struct phyint *pi, struct router *dr)
{
dr->dr_next = pi->pi_router_list;
dr->dr_prev = NULL;
if (pi->pi_router_list != NULL)
pi->pi_router_list->dr_prev = dr;
pi->pi_router_list = dr;
dr->dr_physical = pi;
}
static void
router_delete(struct router *dr)
{
struct phyint *pi;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_delete(%s, %s, %u)\n",
dr->dr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_lifetime);
}
pi = dr->dr_physical;
if (dr->dr_inkernel && (pi->pi_kernel_state & PI_PRESENT))
router_delete_k(dr);
if (dr->dr_prev == NULL) {
if (pi != NULL)
pi->pi_router_list = dr->dr_next;
} else {
dr->dr_prev->dr_next = dr->dr_next;
}
if (dr->dr_next != NULL)
dr->dr_next->dr_prev = dr->dr_prev;
dr->dr_next = dr->dr_prev = NULL;
free(dr);
}
void
router_update_k(struct router *dr)
{
char abuf[INET6_ADDRSTRLEN];
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_update_k(%s, %s, %u)\n",
dr->dr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_lifetime);
}
if (dr->dr_lifetime == 0 && dr->dr_inkernel) {
if (dr->dr_physical->pi_num_k_routers == 1) {
logmsg(LOG_WARNING,
"Last default router (%s) removed on %s\n",
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_physical->pi_name);
}
router_delete(dr);
} else if (dr->dr_lifetime != 0 && !dr->dr_inkernel)
router_add_k(dr);
}
uint_t
router_timer(struct router *dr, uint_t elapsed)
{
uint_t next = TIMER_INFINITY;
char abuf[INET6_ADDRSTRLEN];
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_timer(%s, %s, %u, %d)\n",
dr->dr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_lifetime, elapsed);
}
if (dr->dr_lifetime <= elapsed) {
dr->dr_lifetime = 0;
} else {
dr->dr_lifetime -= elapsed;
if (dr->dr_lifetime < next)
next = dr->dr_lifetime;
}
if (dr->dr_lifetime == 0) {
if (dr->dr_physical->pi_num_k_routers == 1) {
logmsg(LOG_WARNING,
"Last default router (%s) timed out on %s\n",
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_physical->pi_name);
}
router_delete(dr);
}
return (next);
}
static void
router_add_k(struct router *dr)
{
struct phyint *pi = dr->dr_physical;
char abuf[INET6_ADDRSTRLEN];
int rlen;
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_add_k(%s, %s, %u)\n",
dr->dr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_lifetime);
}
rta_gateway->sin6_addr = dr->dr_address;
rta_ifp->sdl_index = if_nametoindex(pi->pi_name);
if (rta_ifp->sdl_index == 0) {
logperror_pi(pi, "router_add_k: if_nametoindex");
return;
}
rt_msg->rtm_flags = RTF_GATEWAY;
rt_msg->rtm_type = RTM_ADD;
rt_msg->rtm_seq = ++rtmseq;
rlen = write(rtsock, rt_msg, rt_msg->rtm_msglen);
if (rlen < 0) {
if (errno != EEXIST) {
logperror_pi(pi, "router_add_k: RTM_ADD");
return;
}
} else if (rlen < rt_msg->rtm_msglen) {
logmsg(LOG_ERR, "router_add_k: write to routing socket got "
"only %d for rlen (interface %s)\n", rlen, pi->pi_name);
return;
}
dr->dr_inkernel = _B_TRUE;
pi->pi_num_k_routers++;
}
static void
router_delete_k(struct router *dr)
{
struct phyint *pi = dr->dr_physical;
char abuf[INET6_ADDRSTRLEN];
int rlen;
if (debug & D_ROUTER) {
logmsg(LOG_DEBUG, "router_delete_k(%s, %s, %u)\n",
dr->dr_physical->pi_name,
inet_ntop(AF_INET6, (void *)&dr->dr_address,
abuf, sizeof (abuf)), dr->dr_lifetime);
}
rta_gateway->sin6_addr = dr->dr_address;
rta_ifp->sdl_index = if_nametoindex(pi->pi_name);
if (rta_ifp->sdl_index == 0) {
logperror_pi(pi, "router_delete_k: if_nametoindex");
return;
}
rt_msg->rtm_flags = RTF_GATEWAY;
rt_msg->rtm_type = RTM_DELETE;
rt_msg->rtm_seq = ++rtmseq;
rlen = write(rtsock, rt_msg, rt_msg->rtm_msglen);
if (rlen < 0) {
if (errno != ESRCH) {
logperror_pi(pi, "router_delete_k: RTM_DELETE");
}
} else if (rlen < rt_msg->rtm_msglen) {
logmsg(LOG_ERR, "router_delete_k: write to routing socket got "
"only %d for rlen (interface %s)\n", rlen, pi->pi_name);
}
dr->dr_inkernel = _B_FALSE;
pi->pi_num_k_routers--;
}
static void
router_print(struct router *dr)
{
char abuf[INET6_ADDRSTRLEN];
logmsg(LOG_DEBUG, "Router %s on %s inkernel %d lifetime %u\n",
inet_ntop(AF_INET6, (void *)&dr->dr_address, abuf, sizeof (abuf)),
dr->dr_physical->pi_name, dr->dr_inkernel, dr->dr_lifetime);
}
void
phyint_print_all(void)
{
struct phyint *pi;
for (pi = phyints; pi != NULL; pi = pi->pi_next) {
phyint_print(pi);
}
}
void
phyint_cleanup(struct phyint *pi)
{
pi->pi_state = 0;
pi->pi_kernel_state = 0;
if (pi->pi_AdvSendAdvertisements) {
check_to_advertise(pi, ADV_OFF);
} else {
check_to_solicit(pi, SOLICIT_OFF);
}
while (pi->pi_router_list)
router_delete(pi->pi_router_list);
(void) poll_remove(pi->pi_sock);
(void) close(pi->pi_sock);
pi->pi_sock = -1;
pi->pi_stateless = pi->pi_StatelessAddrConf;
pi->pi_stateful = pi->pi_StatefulAddrConf;
pi->pi_ipadm_aobjname[0] = '\0';
pi->pi_ifaddr = in6addr_any;
}
void
prefix_update_ipadm_addrobj(struct prefix *pr, boolean_t add)
{
struct phyint *pi = pr->pr_physical;
int lnum = 0;
char *cp;
ipadm_handle_t iph;
ipadm_status_t status;
if (pi->pi_ipadm_aobjname[0] == '\0' ||
pr->pr_name[0] == '\0' || IN6_IS_ADDR_LINKLOCAL(&pr->pr_address) ||
(!(pr->pr_flags & IFF_ADDRCONF) &&
!(pr->pr_flags & IFF_DHCPRUNNING))) {
return;
}
if ((status = ipadm_open(&iph, 0)) != IPADM_SUCCESS) {
logmsg(LOG_ERR, "Could not open handle to libipadm: %s\n",
ipadm_status2str(status));
return;
}
cp = strrchr(pr->pr_name, ':');
if (cp != NULL)
lnum = atoi(++cp);
if (add) {
status = ipadm_add_aobjname(iph, pi->pi_name, AF_INET6,
pi->pi_ipadm_aobjname, IPADM_ADDR_IPV6_ADDRCONF, lnum);
} else {
status = ipadm_delete_aobjname(iph, pi->pi_name, AF_INET6,
pi->pi_ipadm_aobjname, IPADM_ADDR_IPV6_ADDRCONF, lnum);
}
if (status != IPADM_SUCCESS && status != IPADM_IPC_ERROR) {
logmsg(LOG_ERR, "ipadm error in %s '%s' : %s\n",
(add ? "adding" : "deleting"), pi->pi_ipadm_aobjname,
ipadm_status2str(status));
}
ipadm_close(iph);
}