Example #1
0
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);
	}
}
Example #2
0
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);
	}
}
Example #3
0
/*
 * 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);
	}
}
Example #4
0
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);
}
Example #5
0
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 );
}
Example #6
0
File: inLib.c Project: phoboz/vmx
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;
}
Example #7
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);
}