void rtfree(struct rtentry *rt) { struct ifaddr *ifa; if (rt == NULL) panic("rtfree"); rt->rt_refcnt--; if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { if (rt->rt_refcnt == 0 && (rt->rt_nodes->rn_flags & RNF_ACTIVE)) return; /* route still active but currently down */ if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic("rtfree 2"); rttrash--; if (rt->rt_refcnt < 0) { printf("rtfree: %p not freed (neg refs)\n", rt); return; } rt_timer_remove_all(rt); ifa = rt->rt_ifa; if (ifa) IFAFREE(ifa); rtlabel_unref(rt->rt_labelid); #ifdef MPLS if (rt->rt_flags & RTF_MPLS) free(rt->rt_llinfo, M_TEMP); #endif Free(rt_key(rt)); pool_put(&rtentry_pool, rt); } }
void rtfree(struct rtentry *rt) { struct ifaddr *ifa; if (rt == NULL) panic("rtfree"); rt->rt_refcnt--; if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic("rtfree 2"); rttrash--; if (rt->rt_refcnt < 0) { printf("rtfree: %p not freed (neg refs)\n", rt); return; } rt_timer_remove_all(rt); ifa = rt->rt_ifa; if (ifa) IFAFREE(ifa); rtlabel_unref(rt->rt_labelid); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); } }
/* * Default action when installing a route with a Link Level gateway. * Lookup an appropriate real ifa to point to. * This should be moved to /sys/net/link.c eventually. */ static void link_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa) { struct ifaddr *ifa; struct sockaddr *dst; struct ifnet *ifp; if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) return; ifa = ifaof_ifpforaddr(dst, ifp); if (ifa) { IFAFREE(rt->rt_ifa); rt->rt_ifa = ifa; ifa->ifa_refcnt++; if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, sa); } }
int rtrequest1(int req, struct rt_addrinfo *info, u_int8_t prio, struct rtentry **ret_nrt, u_int tableid) { int s = splsoftnet(); int error = 0; struct rtentry *rt, *crt; struct radix_node *rn; struct radix_node_head *rnh; struct ifaddr *ifa; struct sockaddr *ndst; struct sockaddr_rtlabel *sa_rl, sa_rl2; #ifdef MPLS struct sockaddr_mpls *sa_mpls; #endif #define senderr(x) { error = x ; goto bad; } if ((rnh = rt_gettable(info->rti_info[RTAX_DST]->sa_family, tableid)) == NULL) senderr(EAFNOSUPPORT); if (info->rti_flags & RTF_HOST) info->rti_info[RTAX_NETMASK] = NULL; switch (req) { case RTM_DELETE: if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; #ifndef SMALL_KERNEL /* * if we got multipath routes, we require users to specify * a matching RTAX_GATEWAY. */ if (rn_mpath_capable(rnh)) { rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY], prio); rn = (struct radix_node *)rt; if (!rt) senderr(ESRCH); } #endif if ((rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh, rn)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; /* clean up any cloned children */ if ((rt->rt_flags & RTF_CLONING) != 0) rtflushclone(rnh, rt); if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtrequest delete"); if (rt->rt_gwroute) { rt = rt->rt_gwroute; RTFREE(rt); (rt = (struct rtentry *)rn)->rt_gwroute = NULL; } if (rt->rt_parent) { rt->rt_parent->rt_refcnt--; rt->rt_parent = NULL; } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh)) { if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL && rn_mpath_next(rn, 0) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; } #endif rt->rt_flags &= ~RTF_UP; if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt, info); rttrash++; if (ret_nrt) *ret_nrt = rt; else if (rt->rt_refcnt <= 0) { rt->rt_refcnt++; rtfree(rt); } break; case RTM_RESOLVE: if (ret_nrt == NULL || (rt = *ret_nrt) == NULL) senderr(EINVAL); if ((rt->rt_flags & RTF_CLONING) == 0) senderr(EINVAL); ifa = rt->rt_ifa; info->rti_flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC); info->rti_flags |= RTF_CLONED; info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; if ((info->rti_info[RTAX_NETMASK] = rt->rt_genmask) == NULL) info->rti_flags |= RTF_HOST; info->rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl2); goto makeroute; case RTM_ADD: if (info->rti_ifa == 0 && (error = rt_getifa(info, tableid))) senderr(error); ifa = info->rti_ifa; makeroute: rt = pool_get(&rtentry_pool, PR_NOWAIT | PR_ZERO); if (rt == NULL) senderr(ENOBUFS); rt->rt_flags = info->rti_flags; if (prio == 0) prio = ifa->ifa_ifp->if_priority + RTP_STATIC; rt->rt_priority = prio; /* init routing priority */ if ((LINK_STATE_IS_UP(ifa->ifa_ifp->if_link_state) || ifa->ifa_ifp->if_link_state == LINK_STATE_UNKNOWN) && ifa->ifa_ifp->if_flags & IFF_UP) rt->rt_flags |= RTF_UP; else { rt->rt_flags &= ~RTF_UP; rt->rt_priority |= RTP_DOWN; } LIST_INIT(&rt->rt_timer); if (rt_setgate(rt, info->rti_info[RTAX_DST], info->rti_info[RTAX_GATEWAY], tableid)) { pool_put(&rtentry_pool, rt); senderr(ENOBUFS); } ndst = rt_key(rt); if (info->rti_info[RTAX_NETMASK] != NULL) { rt_maskedcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_NETMASK]); } else Bcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_DST]->sa_len); #ifndef SMALL_KERNEL /* do not permit exactly the same dst/mask/gw pair */ if (rn_mpath_capable(rnh) && rt_mpath_conflict(rnh, rt, info->rti_info[RTAX_NETMASK], info->rti_flags & RTF_MPATH)) { if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #endif if (info->rti_info[RTAX_LABEL] != NULL) { sa_rl = (struct sockaddr_rtlabel *) info->rti_info[RTAX_LABEL]; rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label); } #ifdef MPLS /* We have to allocate additional space for MPLS infos */ if (info->rti_info[RTAX_SRC] != NULL || info->rti_info[RTAX_DST]->sa_family == AF_MPLS) { struct rt_mpls *rt_mpls; sa_mpls = (struct sockaddr_mpls *) info->rti_info[RTAX_SRC]; rt->rt_llinfo = (caddr_t)malloc(sizeof(struct rt_mpls), M_TEMP, M_NOWAIT|M_ZERO); if (rt->rt_llinfo == NULL) { if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(ENOMEM); } rt_mpls = (struct rt_mpls *)rt->rt_llinfo; if (sa_mpls != NULL) rt_mpls->mpls_label = sa_mpls->smpls_label; rt_mpls->mpls_operation = info->rti_mpls; /* XXX: set experimental bits */ rt->rt_flags |= RTF_MPLS; } #endif ifa->ifa_refcnt++; rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; if (req == RTM_RESOLVE) { /* * Copy both metrics and a back pointer to the cloned * route's parent. */ rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ rt->rt_priority = (*ret_nrt)->rt_priority; rt->rt_parent = *ret_nrt; /* Back ptr. to parent. */ rt->rt_parent->rt_refcnt++; } rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes, rt->rt_priority); if (rn == NULL && (crt = rtalloc1(ndst, 0, tableid)) != NULL) { /* overwrite cloned route */ if ((crt->rt_flags & RTF_CLONED) != 0) { rtdeletemsg(crt, tableid); rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes, rt->rt_priority); } RTFREE(crt); } if (rn == 0) { IFAFREE(ifa); if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent) rtfree(rt->rt_parent); if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh) && (rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL && (rn = rn_mpath_prio(rn, prio)) != NULL) { if (rn_mpath_next(rn, 0) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; else ((struct rtentry *)rn)->rt_flags |= RTF_MPATH; } #endif if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(req, rt, info); if (ret_nrt) { *ret_nrt = rt; rt->rt_refcnt++; } if ((rt->rt_flags & RTF_CLONING) != 0) { /* clean up any cloned children */ rtflushclone(rnh, rt); } if_group_routechange(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK]); break; } bad: splx(s); return (error); }
int at_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct proc *p ) { struct ifreq *ifr = (struct ifreq *)data; struct sockaddr_at *sat; struct netrange *nr; struct at_aliasreq *ifra = (struct at_aliasreq *)data; struct at_ifaddr *aa0; struct at_ifaddr *aa = 0; struct ifaddr *ifa, *ifa0; /* * If we have an ifp, then find the matching at_ifaddr if it exists */ if ( ifp ) { for ( aa = at_ifaddr; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp ) break; } } /* * In this first switch table we are basically getting ready for * the second one, by getting the atalk-specific things set up * so that they start to look more similar to other protocols etc. */ switch ( cmd ) { case SIOCAIFADDR: case SIOCDIFADDR: /* * If we have an appletalk sockaddr, scan forward of where * we are now on the at_ifaddr list to find one with a matching * address on this interface. * This may leave aa pointing to the first address on the * NEXT interface! */ if ( ifra->ifra_addr.sat_family == AF_APPLETALK ) { for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && sateqaddr( &aa->aa_addr, &ifra->ifra_addr )) { break; } } } /* * If we a retrying to delete an addres but didn't find such, * then rewurn with an error */ if ( cmd == SIOCDIFADDR && aa == 0 ) { return( EADDRNOTAVAIL ); } /*FALLTHROUGH*/ case SIOCSIFADDR: /* * If we are not superuser, then we don't get to do these ops. */ if ( suser(p->p_ucred, &p->p_acflag) ) { return( EPERM ); } sat = satosat( &ifr->ifr_addr ); nr = (struct netrange *)sat->sat_zero; if ( nr->nr_phase == 1 ) { /* * Look for a phase 1 address on this interface. * This may leave aa pointing to the first address on the * NEXT interface! */ for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) { break; } } } else { /* default to phase 2 */ /* * Look for a phase 2 address on this interface. * This may leave aa pointing to the first address on the * NEXT interface! */ for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) { break; } } } if ( ifp == 0 ) panic( "at_control" ); /* * If we failed to find an existing at_ifaddr entry, then we * allocate a fresh one. */ if ( aa == (struct at_ifaddr *) 0 ) { aa0 = malloc(sizeof(struct at_ifaddr), M_IFADDR, M_WAITOK); bzero(aa0, sizeof(struct at_ifaddr)); if (( aa = at_ifaddr ) != NULL ) { /* * Don't let the loopback be first, since the first * address is the machine's default address for * binding. * If it is, stick ourself in front, otherwise * go to the back of the list. */ if ( at_ifaddr->aa_ifp->if_flags & IFF_LOOPBACK ) { aa = aa0; aa->aa_next = at_ifaddr; at_ifaddr = aa; } else { for ( ; aa->aa_next; aa = aa->aa_next ) ; aa->aa_next = aa0; } } else { at_ifaddr = aa0; } /* * Don't Add a reference for the aa itself! * I fell into this trap. IFAFREE tests for <=0 * not <= 1 like RTFREE */ /* aa->aa_ifa.ifa_refcnt++; DON'T DO THIS!! */ aa = aa0; /* * Find the end of the interface's addresses * and link our new one on the end */ ifa = (struct ifaddr *)aa; TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); /* * Add a reference for the linking into the ifp_if_addrlist. */ ifa->ifa_refcnt++; /* * As the at_ifaddr contains the actual sockaddrs, * and the ifaddr itself, link them al together correctly. */ ifa->ifa_addr = (struct sockaddr *)&aa->aa_addr; ifa->ifa_dstaddr = (struct sockaddr *)&aa->aa_addr; ifa->ifa_netmask = (struct sockaddr *)&aa->aa_netmask; /* * Set/clear the phase 2 bit. */ if ( nr->nr_phase == 1 ) { aa->aa_flags &= ~AFA_PHASE2; } else { aa->aa_flags |= AFA_PHASE2; } /* * and link it all together */ aa->aa_ifp = ifp; } else { /* * If we DID find one then we clobber any routes dependent on it.. */ at_scrub( ifp, aa ); } break; case SIOCGIFADDR : sat = satosat( &ifr->ifr_addr ); nr = (struct netrange *)sat->sat_zero; if ( nr->nr_phase == 1 ) { /* * If the request is specifying phase 1, then * only look at a phase one address */ for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) { break; } } } else { /* * default to phase 2 */ for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) { break; } } } if ( aa == (struct at_ifaddr *) 0 ) return( EADDRNOTAVAIL ); break; } /* * By the time this switch is run we should be able to assume that * the "aa" pointer is valid when needed. */ switch ( cmd ) { case SIOCGIFADDR: /* * copy the contents of the sockaddr blindly. */ sat = (struct sockaddr_at *)&ifr->ifr_addr; *sat = aa->aa_addr; /* * and do some cleanups */ ((struct netrange *)&sat->sat_zero)->nr_phase = (aa->aa_flags & AFA_PHASE2) ? 2 : 1; ((struct netrange *)&sat->sat_zero)->nr_firstnet = aa->aa_firstnet; ((struct netrange *)&sat->sat_zero)->nr_lastnet = aa->aa_lastnet; break; case SIOCSIFADDR: return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr )); case SIOCAIFADDR: if ( sateqaddr( &ifra->ifra_addr, &aa->aa_addr )) { return( 0 ); } return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr )); case SIOCDIFADDR: /* * scrub all routes.. didn't we just DO this? XXX yes, del it */ at_scrub( ifp, aa ); /* * remove the ifaddr from the interface */ ifa0 = (struct ifaddr *)aa; TAILQ_REMOVE(&ifp->if_addrhead, ifa0, ifa_link); /* * refs goes from 1->0 if no external refs. note.. * This will not free it ... looks for -1. */ IFAFREE(ifa0); /* * Now remove the at_ifaddr from the parallel structure * as well, or we'd be in deep trouble */ aa0 = aa; if ( aa0 == ( aa = at_ifaddr )) { at_ifaddr = aa->aa_next; } else { while ( aa->aa_next && ( aa->aa_next != aa0 )) { aa = aa->aa_next; } /* * if we found it, remove it, otherwise we screwed up. */ if ( aa->aa_next ) { aa->aa_next = aa0->aa_next; } else { panic( "at_control" ); } } /* * Now dump the memory we were using. * Decrement the reference count. * This should probably be the last reference * as the count will go from 0 to -1. * (unless there is still a route referencing this) */ IFAFREE(ifa0); break; default: if ( ifp == 0 || ifp->if_ioctl == 0 ) return( EOPNOTSUPP ); return( (*ifp->if_ioctl)( ifp, cmd, data )); } return( 0 ); }
int in_control(struct socket *so, unsigned long cmd, void *data, struct ifnet *ifp) { struct ifreq *ifr; struct in_ifaddr *ia, *oia; struct ifaddr *ifa; struct in_aliasreq *ifra; struct sockaddr_in oldaddr; int err, newroute, newmask, newhost; unsigned long i; /* Initialize local */ ifr = (struct ifreq *) data; ia = NULL; ifra = (struct in_aliasreq *) data; err = 0; newroute = 0; /* If interface argument sent */ if (ifp != NULL) { /* For all interfaces */ for (ia = in_ifaddr; ia != NULL; ia = ia->ia_next) if (ia->ia_ifp == ifp) break; } /* End if interface argument sent */ /******************************************/ /* Select command part1 - Initialize data */ /******************************************/ switch(cmd) { case SIOCAIFADDR: if (ia == NULL) newroute = 1; /* FALL TRU */ case SIOCDIFADDR: /* If internet family */ if (ifra->ifra_addr.sin_family == AF_INET) { /* For all interface addresses */ for (oia = ia; ia != NULL; ia = ia->ia_next) { if ((ia->ia_ifp == ifp) && (ia->ia_addr.sin_addr.s_addr == ifra->ifra_addr.sin_addr.s_addr)) break; } /* End for all interface addresses */ } /* End if internet family */ if ( (cmd == SIOCDIFADDR) && (ia == NULL) ) return EADDRNOTAVAIL; /* FALL TRU */ case SIOCSIFADDR: case SIOCSIFNETMASK: case SIOCSIFDSTADDR: if ( (so->so_state & SS_PRIV) == 0 ) return EPERM; if (ifp == NULL) panic("in control"); /* If need to alloc */ if (ia == NULL) { oia = mb_alloc(sizeof(struct in_ifaddr), MT_IFADDR, M_WAIT); if (oia == NULL) return ENOBUFS; } /* End if need to alloc */ /* Clear address */ memset(oia, 0, sizeof(struct in_ifaddr)); /* Add address to address list */ ia = in_ifaddr; /* If any addresses exists */ if (ia != NULL) { /* Go to end of address list */ for (; ia->ia_next != NULL; ia = ia->ia_next) continue; /* Set address */ ia->ia_next = oia; } /* End if any addresses exists */ /* Else no addresses exists */ else { /* Set address */ in_ifaddr = oia; } /* End else no addresses exists */ /* Add address to interface address list */ ia = oia; ifa = ifp->if_addrlist; /* If any addresses in interface address list */ if (ifa != NULL) { /* Move to last address */ for ( ; ifa->ifa_next != NULL; ifa = ifa->ifa_next) continue; /* Set address */ ifa->ifa_next = (struct ifaddr *) ia; } /* End if any addresses in interface address list */ /* Else no addresses in interface address list */ else { /* Set address */ ifp->if_addrlist = (struct ifaddr *) ia; } /* End else no addresses in interface address list */ /* Setup address struct */ ia->ia_ifa.ifa_addr = (struct sockaddr *) &ia->ia_addr; ia->ia_ifa.ifa_dstaddr = (struct sockaddr *) &ia->ia_dstaddr; ia->ia_ifa.ifa_netmask = (struct sockaddr *) &ia->ia_netmask; ia->ia_sockmask.sin_len = 8; /* If broadcast available */ if (ifp->if_flags & IFF_BROADCAST) { ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in); ia->ia_broadaddr.sin_family = AF_INET; } /* End if broadcast available */ /* Set interface point in address */ ia->ia_ifp = ifp; if (ifp != loif) in_interfaces++; break; case SIOCSIFBRDADDR: if ( (so->so_state & SS_PRIV) == 0 ) return EPERM; /* FALL TRU */ case SIOCGIFADDR: case SIOCGIFNETMASK: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: if (ia == NULL) return EADDRNOTAVAIL; break; } /* End select command part1 - Initialize data */ /******************************************/ /* Select command part2 - Execute command */ /******************************************/ switch(cmd) { case SIOCGIFADDR: /* Get address */ *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; break; case SIOCGIFBRDADDR: if ( (ifp->if_flags & IFF_BROADCAST) == 0 ) return EINVAL; /* Get address */ *((struct sockaddr_in *) &ifr->ifr_dstaddr) = ia->ia_broadaddr; break; case SIOCGIFDSTADDR: if ( (ifp->if_flags & IFF_POINTOPOINT) == 0 ) return EINVAL; /* Get address */ *((struct sockaddr_in *) &ifr->ifr_dstaddr) = ia->ia_dstaddr; break; case SIOCGIFNETMASK: /* Get mask */ *((struct sockaddr_in *) &ifr->ifr_addr) = ia->ia_sockmask; break; case SIOCSIFDSTADDR: if ( (ifp->if_flags & IFF_POINTOPOINT) == 0 ) return EINVAL; /* Set address */ oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = *(struct sockaddr_in *) &ifr->ifr_dstaddr; /* If interface ioctl fails */ if ( (ifp->if_ioctl != NULL) && (err = ( *ifp->if_ioctl) (ifp, SIOCSIFDSTADDR, ia)) ) { ia->ia_dstaddr = oldaddr; return err; } /* If interface ioctl fails */ /* If route */ if (ia->ia_flags & IFA_ROUTE) { /* Delete old route */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *) &oldaddr; rtinit(&(ia->ia_ifa), RTM_DELETE, RTF_HOST); /* Add new route */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *) &ia->ia_dstaddr; rtinit(&(ia->ia_ifa), RTM_ADD, RTF_HOST | RTF_UP); } /* If route */ break; case SIOCSIFBRDADDR: if ( (ifp->if_flags & IFF_BROADCAST) == 0 ) return EINVAL; /* Set address */ ia->ia_broadaddr = *(struct sockaddr_in *) &ifr->ifr_broadaddr; break; case SIOCSIFADDR: return in_ifinit(ifp, ia, (struct sockaddr_in *) &ifr->ifr_addr, 1); case SIOCSIFNETMASK: i = ifra->ifra_addr.sin_addr.s_addr; ia->ia_sockmask.sin_addr.s_addr = i; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); in_socktrim(&ia->ia_sockmask); break; case SIOCAIFADDR: newmask = 0; newhost = 1; /* If inet family */ if (ia->ia_addr.sin_family == AF_INET) { /* If zero length */ if (ifra->ifra_addr.sin_len == 0) { ifra->ifra_addr = ia->ia_addr; newhost = 0; } /* End if zero length */ /* Else if address match */ else if (ifra->ifra_addr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) { newhost = 0; } /* End else if address match */ } /* End if inet family */ /* If mask length non-zero */ if (ifra->ifra_mask.sin_len) { in_ifscrub(ifp, ia); ia->ia_sockmask = ifra->ifra_mask; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); newmask = 1; } /* End if mask length non-zero */ /* If point-to-point and inet */ if ( (ifp->if_flags & IFF_POINTOPOINT) && (ifra->ifra_dstaddr.sin_family == AF_INET) ) { in_ifscrub(ifp, ia); ia->ia_dstaddr = ifra->ifra_dstaddr; newmask = 1; } /* End if point-to-point and inet */ if ( (ifra->ifra_addr.sin_family == AF_INET) && (newhost || newmask) ) err = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); if ( (err == EEXIST) && !newroute) err = 0; if ( (ifp->if_flags & IFF_BROADCAST) && (ifra->ifra_broadaddr.sin_family == AF_INET) && (ifra->ifra_broadaddr.sin_addr.s_addr) ) ia->ia_broadaddr = ifra->ifra_broadaddr; return err; case SIOCDIFADDR: in_ifscrub(ifp, ia); /* Get interface address list */ ifa = ifp->if_addrlist; /* If the same address list */ if (ifa == (struct ifaddr *) ia) { ifp->if_addrlist = ifa->ifa_next; } /* End if the same address list */ /* Else not the same address list */ else { /* Move until end or match */ while ( (ifa->ifa_next != NULL) && (ifa->ifa_next != (struct ifaddr *) ia) ) ifa = ifa->ifa_next; if (ifa->ifa_next != NULL) ifa->ifa_next = ((struct ifaddr *) ia)->ifa_next; else panic("link inifaddr"); } /* End else not the same address list */ oia = ia; ia = in_ifaddr; /* If old is first element */ if (oia == ia) { in_ifaddr = ia->ia_next; } /* End if old is first element */ /* Else old is not first element */ else { /* Move until end or match */ while ( (ia->ia_next != NULL) && (ia->ia_next != oia) ) ia = ia->ia_next; if (ia->ia_next != NULL) ia->ia_next = oia->ia_next; else panic("unlink inifaddr"); } /* End else old is not first element */ IFAFREE(&oia->ia_ifa); break; default: if ( (ifp == NULL) || (ifp->if_ioctl == NULL) ) return EOPNOTSUPP; return ( *ifp->if_ioctl) (ifp, cmd, data); } /* End select command part2 - Execute command */ return 0; }
int rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, u_int tableid) { int s = splsoftnet(); int error = 0; struct rtentry *rt, *crt; struct radix_node *rn; struct radix_node_head *rnh; struct ifaddr *ifa; struct sockaddr *ndst; struct sockaddr_rtlabel *sa_rl; #define senderr(x) { error = x ; goto bad; } if ((rnh = rt_gettable(info->rti_info[RTAX_DST]->sa_family, tableid)) == NULL) senderr(EAFNOSUPPORT); if (info->rti_flags & RTF_HOST) info->rti_info[RTAX_NETMASK] = NULL; switch (req) { case RTM_DELETE: if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; #ifndef SMALL_KERNEL /* * if we got multipath routes, we require users to specify * a matching RTAX_GATEWAY. */ if (rn_mpath_capable(rnh)) { rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); rn = (struct radix_node *)rt; if (!rt) senderr(ESRCH); } #endif if ((rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh, rn)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; /* clean up any cloned children */ if ((rt->rt_flags & RTF_CLONING) != 0) rtflushclone(rnh, rt); if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtrequest delete"); if (rt->rt_gwroute) { rt = rt->rt_gwroute; RTFREE(rt); (rt = (struct rtentry *)rn)->rt_gwroute = NULL; } if (rt->rt_parent) { rt->rt_parent->rt_refcnt--; rt->rt_parent = NULL; } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh)) { if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL && rn_mpath_next(rn) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; } #endif rt->rt_flags &= ~RTF_UP; if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt, info); rttrash++; if (ret_nrt) *ret_nrt = rt; else if (rt->rt_refcnt <= 0) { rt->rt_refcnt++; rtfree(rt); } break; case RTM_RESOLVE: if (ret_nrt == NULL || (rt = *ret_nrt) == NULL) senderr(EINVAL); if ((rt->rt_flags & RTF_CLONING) == 0) senderr(EINVAL); ifa = rt->rt_ifa; info->rti_flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC); info->rti_flags |= RTF_CLONED; info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; if ((info->rti_info[RTAX_NETMASK] = rt->rt_genmask) == NULL) info->rti_flags |= RTF_HOST; goto makeroute; case RTM_ADD: if (info->rti_ifa == 0 && (error = rt_getifa(info))) senderr(error); ifa = info->rti_ifa; makeroute: rt = pool_get(&rtentry_pool, PR_NOWAIT); if (rt == NULL) senderr(ENOBUFS); Bzero(rt, sizeof(*rt)); rt->rt_flags = RTF_UP | info->rti_flags; LIST_INIT(&rt->rt_timer); if (rt_setgate(rt, info->rti_info[RTAX_DST], info->rti_info[RTAX_GATEWAY], tableid)) { pool_put(&rtentry_pool, rt); senderr(ENOBUFS); } ndst = rt_key(rt); if (info->rti_info[RTAX_NETMASK] != NULL) { rt_maskedcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_NETMASK]); } else Bcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_DST]->sa_len); #ifndef SMALL_KERNEL /* do not permit exactly the same dst/mask/gw pair */ if (rn_mpath_capable(rnh) && rt_mpath_conflict(rnh, rt, info->rti_info[RTAX_NETMASK], info->rti_flags & RTF_MPATH)) { if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #endif if (info->rti_info[RTAX_LABEL] != NULL) { sa_rl = (struct sockaddr_rtlabel *) info->rti_info[RTAX_LABEL]; rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label); } ifa->ifa_refcnt++; rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; if (req == RTM_RESOLVE) { /* * Copy both metrics and a back pointer to the cloned * route's parent. */ rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ rt->rt_parent = *ret_nrt; /* Back ptr. to parent. */ rt->rt_parent->rt_refcnt++; } rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes); if (rn == NULL && (crt = rtalloc1(ndst, 0, tableid)) != NULL) { /* overwrite cloned route */ if ((crt->rt_flags & RTF_CLONED) != 0) { rtdeletemsg(crt, tableid); rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes); } RTFREE(crt); } if (rn == 0) { IFAFREE(ifa); if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent) rtfree(rt->rt_parent); if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh) && (rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL) { if (rn_mpath_next(rn) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; else ((struct rtentry *)rn)->rt_flags |= RTF_MPATH; } #endif if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(req, rt, info); if (ret_nrt) { *ret_nrt = rt; rt->rt_refcnt++; } if ((rt->rt_flags & RTF_CLONING) != 0) { /* clean up any cloned children */ rtflushclone(rnh, rt); } if_group_routechange(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK]); break; } bad: splx(s); return (error); }