* Copyright (c) 1980, 1986 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)route.c 7.7 (Berkeley) %G%
#include "../netinet/in.h"
#include "../netinet/in_var.h"
int rttrash
; /* routes not in table but not freed */
struct sockaddr wildcard
; /* zero valued cookie for wildcard searches */
int rthashsize
= RTHASHSIZ
; /* for netstat, etc. */
static int rtinits_done
= 0;
struct radix_node_head
*ns_rnhead
, *in_rnhead
;
rn_inithead(&ns_rnhead
, 16, AF_NS
) &&
rn_inithead(&in_rnhead
, 32, AF_INET
))
* Packet routing routines.
register struct route
*ro
;
register struct radix_node_head
*rnh
;
register struct radix_node
*rn
;
register struct rtentry
*rt
= 0;
u_char af
= ro
->ro_dst
.sa_family
;
if (ro
->ro_rt
&& ro
->ro_rt
->rt_ifp
&& (ro
->ro_rt
->rt_flags
& RTF_UP
))
for (rnh
= radix_node_head
; rnh
&& (af
!= rnh
->rnh_af
); )
if (rnh
&& rnh
->rnh_treetop
&&
(rn
= rn_match((char *)&(ro
->ro_dst
), rnh
->rnh_treetop
)) &&
((rn
->rn_flags
& RNF_ROOT
) == 0)) {
rt
= &(((struct nrtentry
*)rn
)->nrt_rt
);
register struct rtentry
*rt
;
register struct nrtentry
*nrt
;
if (rt
->rt_refcnt
<= 0 && (rt
->rt_flags
&RTF_UP
) == 0) {
nrt
= (struct nrtentry
*) (((struct radix_node
*)rt
) - 2);
if (nrt
->nrt_nodes
->rn_flags
& (RNF_ACTIVE
| RNF_ROOT
))
free((caddr_t
)nrt
, M_RTABLE
);
* Force a routing table entry to the specified
* destination to go through the given gateway.
* Normally called as a result of a routing redirect
* message from the network layer.
* N.B.: must be called at splnet
rtredirect(dst
, gateway
, flags
, src
)
struct sockaddr
*dst
, *gateway
, *src
;
register struct rtentry
*rt
;
/* verify the gateway is directly reachable */
if (ifa_ifwithnet(gateway
) == 0) {
rtstat
.rts_badredirect
++;
(bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
* If the redirect isn't from our current router for this dst,
* it's either old or wrong. If it redirects us to ourselves,
* we have a routing loop, perhaps as a result of an interface
if ((rt
&& !equal(src
, &rt
->rt_gateway
)) || ifa_ifwithaddr(gateway
)) {
rtstat
.rts_badredirect
++;
* Create a new entry if we just got back a wildcard entry
* or the the lookup failed. This is necessary for hosts
* which use routing redirects generated by smart gateways
* to dynamically build the routing tables.
* If we survived the previous tests, it doesn't matter
* what sort of entry we got when we looked it up;
* we should just go ahead and free the reference to
* the route we created. rtalloc will not give a
* pointer to the root node. And if we got a pointer
* to a default gateway, we should free the reference
rtinit(dst
, gateway
, (int)SIOCADDRT
,
(flags
& RTF_HOST
) | RTF_GATEWAY
| RTF_DYNAMIC
);
* Don't listen to the redirect if it's
* for a route to an interface.
if (rt
->rt_flags
& RTF_GATEWAY
) {
if (((rt
->rt_flags
& RTF_HOST
) == 0) && (flags
& RTF_HOST
)) {
* Changing from route to net => route to host.
* Create new route, rather than smashing route to net.
rtinit(dst
, gateway
, (int)SIOCADDRT
,
* Smash the current notion of the gateway to
rt
->rt_gateway
= *gateway
; /*XXX -- size? */
rt
->rt_flags
|= RTF_MODIFIED
;
rtstat
.rts_badredirect
++;
* Routing table ioctl interface.
if (cmd
!= SIOCADDRT
&& cmd
!= SIOCDELRT
)
return (rtrequest(cmd
, (struct rtentry
*)data
));
* This routine will go away soon.
* Tries to guess which netmask is appropriate for a given net.
static struct sockaddr_in rtgmask
= { 8, 0 };
register struct sockaddr
*sa
;
register struct ifaddr
*ifa
;
u_long i
, net
, mask
, subnet
;
register struct in_ifaddr
*ia
;
i
= ntohl(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
);
rtgmask
.sin_addr
.s_addr
= 0;
return ((char *)&rtgmask
);
} else if (IN_CLASSA(i
)) {
} else if (IN_CLASSB(i
)) {
} else if (IN_CLASSC(i
)) {
* Check whether network is a subnet;
* if so, return subnet number.
for (ia
= in_ifaddr
; ia
; ia
= ia
->ia_next
)
rtgmask
.sin_addr
.s_addr
= ntohl(mask
);
return ((char *)&rtgmask
);
return ((char *)ifa
->ifa_netmask
);
* Carry out a request to change the routing table. Called by
* interfaces at boot time to make their ``local routes'' known,
* for ioctl's, and as the result of routing redirects.
register struct rtentry
*entry
;
register struct rtentry
*rt
;
struct ifaddr
*ifa_ifwithdstaddr();
register struct nrtentry
*nrt
;
register struct radix_node
*rn
;
register struct radix_node_head
*rnh
;
#if BYTE_ORDER != BIG_ENDIAN
if (entry
->rt_dst
.sa_family
== 0 && entry
->rt_dst
.sa_len
< 16) {
entry
->rt_dst
.sa_family
= entry
->rt_dst
.sa_len
;
entry
->rt_dst
.sa_len
= 16;
if (entry
->rt_gateway
.sa_family
== 0 && entry
->rt_gateway
.sa_len
< 16) {
entry
->rt_gateway
.sa_family
= entry
->rt_gateway
.sa_len
;
entry
->rt_gateway
.sa_len
= 16;
if (entry
->rt_dst
.sa_len
== 0)
entry
->rt_dst
.sa_len
= 16;
if (entry
->rt_gateway
.sa_len
== 0)
entry
->rt_gateway
.sa_len
= 16;
af
= entry
->rt_dst
.sa_family
;
for (rnh
= radix_node_head
; rnh
&& (af
!= rnh
->rnh_af
); )
if ((entry
->rt_flags
& RTF_GATEWAY
) == 0) {
* If we are adding a route to an interface,
* and the interface is a pt to pt link
* we should search for the destination
* as our clue to the interface. Otherwise
* we can use the local address.
if (entry
->rt_flags
& RTF_HOST
)
ifa
= ifa_ifwithdstaddr(&entry
->rt_dst
);
ifa
= ifa_ifwithaddr(&entry
->rt_gateway
);
* If we are adding a route to a remote net
* or host, the gateway may still be on the
* other end of a pt to pt link.
ifa
= ifa_ifwithdstaddr(&entry
->rt_gateway
);
ifa
= ifa_ifwithnet(&entry
->rt_gateway
);
if (ifa
== 0 && req
== SIOCADDRT
) {
if (entry
->rt_flags
& RTF_HOST
)
netmask
= rtgetmask(&entry
->rt_dst
, ifa
);
if ((rn
= rn_delete((char *)&entry
->rt_dst
,
if (rn
->rn_flags
& (RNF_ACTIVE
| RNF_ROOT
))
panic ("rtrequest delete");
nrt
= (struct nrtentry
*)rn
;
nrt
->nrt_rt
.rt_flags
&= ~RTF_UP
;
if (nrt
->nrt_rt
.rt_refcnt
> 0)
free((caddr_t
)nrt
, M_RTABLE
);
Malloc(nrt
, struct nrtentry
*, sizeof *nrt
);
rn
= rn_addroute((char *)&entry
->rt_dst
, netmask
,
free((caddr_t
)nrt
, M_RTABLE
);
rn
->rn_key
= (char *)&(nrt
->nrt_rt
.rt_dst
);
rt
->rt_dst
= entry
->rt_dst
;
rt
->rt_gateway
= entry
->rt_gateway
;
(entry
->rt_flags
& (RTF_HOST
|RTF_GATEWAY
|RTF_DYNAMIC
));
rt
->rt_ifp
= ifa
->ifa_ifp
;
* Set up a routing table entry, normally
rtinit(dst
, gateway
, cmd
, flags
)
struct sockaddr
*dst
, *gateway
;
bzero((caddr_t
)&route
, sizeof (route
));
route
.rt_gateway
= *gateway
;
(void) rtrequest(cmd
, &route
);