📄 route.c
字号:
void *vp;
{
struct rtentry *rt = (struct rtentry *)rn;
struct rtentry *rt0 = vp;
if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) {
return rtrequest(RTM_DELETE, rt_key(rt),
(struct sockaddr *)0, rt_mask(rt),
rt->rt_flags, (struct rtentry **)0);
}
return 0;
}
/*
* This routine is called from rt_setgate() to do the analogous thing for
* adds and changes. There is the added complication in this case of a
* middle insert; i.e., insertion of a new network route between an older
* network route and (cloned) host routes. For this reason, a simple check
* of rt->rt_parent is insufficient; each candidate route must be tested
* against the (mask, value) of the new route (passed as before in vp)
* to see if the new route matches it.
*
* XXX - it may be possible to do fixdelete() for changes and reserve this
* routine just for adds. I'm not sure why I thought it was necessary to do
* changes this way.
*/
#ifdef DEBUG
static int rtfcdebug = 0;
#endif
static int
rt_fixchange(rn, vp)
struct radix_node *rn;
void *vp;
{
struct rtentry *rt = (struct rtentry *)rn;
struct rtfc_arg *ap = vp;
struct rtentry *rt0 = ap->rt0;
struct radix_node_head *rnh = ap->rnh;
u_char *xk1, *xm1, *xk2, *xmp;
int i, len, mlen;
#ifdef DEBUG
if (rtfcdebug)
printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0);
#endif
if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) {
#ifdef DEBUG
if(rtfcdebug) printf("no parent or pinned\n");
#endif
return 0;
}
if (rt->rt_parent == rt0) {
#ifdef DEBUG
if(rtfcdebug) printf("parent match\n");
#endif
return rtrequest(RTM_DELETE, rt_key(rt),
(struct sockaddr *)0, rt_mask(rt),
rt->rt_flags, (struct rtentry **)0);
}
/*
* There probably is a function somewhere which does this...
* if not, there should be.
*/
len = imin(((struct sockaddr *)rt_key(rt0))->sa_len,
((struct sockaddr *)rt_key(rt))->sa_len);
xk1 = (u_char *)rt_key(rt0);
xm1 = (u_char *)rt_mask(rt0);
xk2 = (u_char *)rt_key(rt);
/* avoid applying a less specific route */
xmp = (u_char *)rt_mask(rt->rt_parent);
mlen = ((struct sockaddr *)rt_key(rt->rt_parent))->sa_len;
if (mlen > ((struct sockaddr *)rt_key(rt0))->sa_len) {
#ifdef DEBUG
if (rtfcdebug)
printf("rt_fixchange: inserting a less "
"specific route\n");
#endif
return 0;
}
for (i = rnh->rnh_treetop->rn_offset; i < mlen; i++) {
if ((xmp[i] & ~(xmp[i] ^ xm1[i])) != xmp[i]) {
#ifdef DEBUG
if (rtfcdebug)
printf("rt_fixchange: inserting a less "
"specific route\n");
#endif
return 0;
}
}
for (i = rnh->rnh_treetop->rn_offset; i < len; i++) {
if ((xk2[i] & xm1[i]) != xk1[i]) {
#ifdef DEBUG
if(rtfcdebug) printf("no match\n");
#endif
return 0;
}
}
/*
* OK, this node is a clone, and matches the node currently being
* changed/added under the node's mask. So, get rid of it.
*/
#ifdef DEBUG
if(rtfcdebug) printf("deleting\n");
#endif
return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0,
rt_mask(rt), rt->rt_flags, (struct rtentry **)0);
}
int
rt_setgate(rt0, dst, gate)
struct rtentry *rt0;
struct sockaddr *dst, *gate;
{
caddr_t new, old;
int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len);
register struct rtentry *rt = rt0;
struct radix_node_head *rnh = rt_tables[dst->sa_family];
/*
* A host route with the destination equal to the gateway
* will interfere with keeping LLINFO in the routing
* table, so disallow it.
*/
if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) ==
(RTF_HOST|RTF_GATEWAY)) &&
(dst->sa_len == gate->sa_len) &&
(bcmp(dst, gate, dst->sa_len) == 0)) {
/*
* The route might already exist if this is an RTM_CHANGE
* or a routing redirect, so try to delete it.
*/
if (rt_key(rt0))
rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0),
rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0);
return EADDRNOTAVAIL;
}
/*
* Both dst and gateway are stored in the same malloc'd chunk
* (If I ever get my hands on....)
* if we need to malloc a new chunk, then keep the old one around
* till we don't need it any more.
*/
if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) {
old = (caddr_t)rt_key(rt);
R_Malloc(new, caddr_t, dlen + glen);
if (new == 0)
return ENOBUFS;
rt->rt_nodes->rn_key = new;
} else {
/*
* otherwise just overwrite the old one
*/
new = rt->rt_nodes->rn_key;
old = 0;
}
/*
* copy the new gateway value into the memory chunk
*/
Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen);
/*
* if we are replacing the chunk (or it's new) we need to
* replace the dst as well
*/
if (old) {
Bcopy(dst, new, dlen);
Free(old);
}
/*
* If there is already a gwroute, it's now almost definitly wrong
* so drop it.
*/
if (rt->rt_gwroute) {
rt = rt->rt_gwroute; RTFREE(rt);
rt = rt0; rt->rt_gwroute = 0;
}
/*
* Cloning loop avoidance:
* In the presence of protocol-cloning and bad configuration,
* it is possible to get stuck in bottomless mutual recursion
* (rtrequest rt_setgate rtalloc1). We avoid this by not allowing
* protocol-cloning to operate for gateways (which is probably the
* correct choice anyway), and avoid the resulting reference loops
* by disallowing any route to run through itself as a gateway.
* This is obviously mandatory when we get rt->rt_output().
*/
if (rt->rt_flags & RTF_GATEWAY) {
rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING);
if (rt->rt_gwroute == rt) {
RTFREE(rt->rt_gwroute);
rt->rt_gwroute = 0;
#define EDQUOT ENFILE
return EDQUOT; /* failure */
}
}
/*
* This isn't going to do anything useful for host routes, so
* don't bother. Also make sure we have a reasonable mask
* (we don't yet have one during adds).
*/
if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) {
struct rtfc_arg arg;
arg.rnh = rnh;
arg.rt0 = rt;
rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt),
rt_fixchange, &arg);
}
return 0;
}
static void
rt_maskedcopy(src, dst, netmask)
struct sockaddr *src, *dst, *netmask;
{
register u_char *cp1 = (u_char *)src;
register u_char *cp2 = (u_char *)dst;
register u_char *cp3 = (u_char *)netmask;
u_char *cplim = cp2 + *cp3;
u_char *cplim2 = cp2 + *cp1;
*cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
cp3 += 2;
if (cplim > cplim2)
cplim = cplim2;
while (cp2 < cplim)
*cp2++ = *cp1++ & *cp3++;
if (cp2 < cplim2)
bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2));
}
/*
* Set up a routing table entry, normally
* for an interface.
*/
int
rtinit(ifa, cmd, flags)
register struct ifaddr *ifa;
int cmd, flags;
{
register struct rtentry *rt;
register struct sockaddr *dst;
register struct sockaddr *deldst;
struct mbuf *m = 0;
struct rtentry *nrt = 0;
int error;
dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
/*
* If it's a delete, check that if it exists, it's on the correct
* interface or we might scrub a route to another ifa which would
* be confusing at best and possibly worse.
*/
if (cmd == RTM_DELETE) {
/*
* It's a delete, so it should already exist..
* If it's a net, mask off the host bits
* (Assuming we have a mask)
*/
if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
m = m_get(M_DONTWAIT, MT_SONAME);
if (m == NULL)
return(ENOBUFS);
deldst = mtod(m, struct sockaddr *);
rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
dst = deldst;
}
/*
* Get an rtentry that is in the routing tree and
* contains the correct info. (if this fails, can't get there).
* We set "report" to FALSE so that if it doesn't exist,
* it doesn't report an error or clone a route, etc. etc.
*/
rt = rtalloc1(dst, 0, 0UL);
if (rt) {
/*
* Ok so we found the rtentry. it has an extra reference
* for us at this stage. we won't need that so
* lop that off now.
*/
rt->rt_refcnt--;
if (rt->rt_ifa != ifa) {
/*
* If the interface in the rtentry doesn't match
* the interface we are using, then we don't
* want to delete it, so return an error.
* This seems to be the only point of
* this whole RTM_DELETE clause.
*/
if (m)
(void) m_free(m);
return (flags & RTF_HOST ? EHOSTUNREACH
: ENETUNREACH);
}
}
/* XXX */
#if 0
else {
/*
* One would think that as we are deleting, and we know
* it doesn't exist, we could just return at this point
* with an "ELSE" clause, but apparently not..
*/
return (flags & RTF_HOST ? EHOSTUNREACH
: ENETUNREACH);
}
#endif
}
/*
* Do the actual request
*/
error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
flags | ifa->ifa_flags, &nrt);
if (m)
(void) m_free(m);
/*
* If we are deleting, and we found an entry, then
* it's been removed from the tree.. now throw it away.
*/
if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) {
/*
* notify any listenning routing agents of the change
*/
rt_newaddrmsg(cmd, ifa, error, nrt);
if (rt->rt_refcnt <= 0) {
rt->rt_refcnt++; /* need a 1->0 transition to free */
rtfree(rt);
}
}
/*
* We are adding, and we have a returned routing entry.
* We need to sanity check the result.
*/
if (cmd == RTM_ADD && error == 0 && (rt = nrt)) {
/*
* We just wanted to add it.. we don't actually need a reference
*/
rt->rt_refcnt--;
/*
* If it came back with an unexpected interface, then it must
* have already existed or something. (XXX)
*/
if (rt->rt_ifa != ifa) {
if (!(rt->rt_ifa->ifa_ifp->if_flags &
(IFF_POINTOPOINT|IFF_LOOPBACK)))
printf("rtinit: wrong ifa (%p) was (%p)\n",
ifa, rt->rt_ifa);
/*
* Ask that the protocol in question
* remove anything it has associated with
* this route and ifaddr.
*/
if (rt->rt_ifa->ifa_rtrequest)
rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
/*
* Remove the reference to its ifaddr.
*/
IFAFREE(rt->rt_ifa);
/*
* And substitute in references to the ifaddr
* we are adding.
*/
rt->rt_ifa = ifa;
rt->rt_ifp = ifa->ifa_ifp;
rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu; /*XXX*/
ifa->ifa_refcnt++;
/*
* Now ask the protocol to check if it needs
* any special processing in its new form.
*/
if (ifa->ifa_rtrequest)
ifa->ifa_rtrequest(RTM_ADD, rt, SA(0));
}
/*
* notify any listenning routing agents of the change
*/
rt_newaddrmsg(cmd, ifa, error, nrt);
}
return (error);
}
/* This must be before ip6_init2(), which is now SI_ORDER_MIDDLE */
SYSINIT(route, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, call_route_init, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -