* Copyright (c) 1980, 1986, 1991 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)route.c 7.32 (Berkeley) %G%
#include <sys/socketvar.h>
#include <netinet/in_var.h>
#define SA(p) ((struct sockaddr *)(p))
int rttrash
; /* routes not in table but not freed */
struct sockaddr wildcard
; /* zero valued cookie for wildcard searches */
for (dom
= domains
; dom
; dom
= dom
->dom_next
)
dom
->dom_rtattach(&table
[dom
->dom_family
],
rn_init(); /* initialize all zeroes, all ones, mask table */
rtable_init((void **)rt_tables
);
* Packet routing routines.
register struct route
*ro
;
if (ro
->ro_rt
&& ro
->ro_rt
->rt_ifp
&& (ro
->ro_rt
->rt_flags
& RTF_UP
))
ro
->ro_rt
= rtalloc1(&ro
->ro_dst
, 1);
register struct sockaddr
*dst
;
register struct radix_node_head
*rnh
= rt_tables
[dst
->sa_family
];
register struct rtentry
*rt
;
register struct radix_node
*rn
;
struct rtentry
*newrt
= 0;
int s
= splnet(), err
= 0, msgtype
= RTM_MISS
;
if (rnh
&& rnh
->rnh_treetop
&&
(rn
= rnh
->rnh_match((caddr_t
)dst
, rnh
->rnh_treetop
)) &&
((rn
->rn_flags
& RNF_ROOT
) == 0)) {
newrt
= rt
= (struct rtentry
*)rn
;
if (report
&& (rt
->rt_flags
& RTF_CLONING
)) {
err
= rtrequest(RTM_RESOLVE
, dst
, SA(0),
if ((rt
= newrt
) && (rt
->rt_flags
& RTF_XRESOLVE
)) {
bzero((caddr_t
)&info
, sizeof(info
));
info
.rti_info
[RTAX_DST
] = dst
;
rt_missmsg(msgtype
, &info
, 0, err
);
register struct rtentry
*rt
;
register struct ifaddr
*ifa
;
if (rt
->rt_refcnt
<= 0 && (rt
->rt_flags
& RTF_UP
) == 0) {
if (rt
->rt_nodes
->rn_flags
& (RNF_ACTIVE
| RNF_ROOT
))
printf("rtfree: %x not freed (neg refs)\n", rt
);
* We are still debugging potential overfreeing of ifaddr's
register struct ifaddr
*ifa
;
printf("ifafree: %x ref %d\n", ifa
, ifa
->ifa_refcnt
);
if (ifa
->ifa_refcnt
== 0 && ifafree_verbose
)
printf("ifafree: %x not freed.\n", ifa
);
* 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
, netmask
, flags
, src
, rtp
)
struct sockaddr
*dst
, *gateway
, *netmask
, *src
;
register struct rtentry
*rt
;
/* verify the gateway is directly reachable */
if (ifa_ifwithnet(gateway
) == 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
#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
if (!(flags
& RTF_DONE
) && rt
&& !equal(src
, rt
->rt_gateway
))
else if (ifa_ifwithaddr(gateway
))
* 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 ((rt
== 0) || (rt_mask(rt
) && rt_mask(rt
)->sa_len
< 2))
* 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.
flags
|= RTF_GATEWAY
| RTF_DYNAMIC
;
error
= rtrequest((int)RTM_ADD
, dst
, gateway
,
stat
= &rtstat
.rts_dynamic
;
* Smash the current notion of the gateway to
* this destination. Should check about netmask!!!
rt
->rt_flags
|= RTF_MODIFIED
;
stat
= &rtstat
.rts_newgateway
;
rt_setgate(rt
, rt_key(rt
), gateway
);
rtstat
.rts_badredirect
++;
bzero((caddr_t
)&info
, sizeof(info
));
info
.rti_info
[RTAX_DST
] = dst
;
info
.rti_info
[RTAX_GATEWAY
] = gateway
;
info
.rti_info
[RTAX_NETMASK
] = netmask
;
info
.rti_info
[RTAX_AUTHOR
] = src
;
rt_missmsg(RTM_REDIRECT
, &info
, flags
, error
);
* Routing table ioctl interface.
register struct ortentry
*entry
= (struct ortentry
*)data
;
struct sockaddr
*netmask
= 0;
else if (req
== SIOCDELRT
)
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
#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;
if ((entry
->rt_flags
& RTF_HOST
) == 0)
switch (entry
->rt_dst
.sa_family
) {
extern struct sockaddr_in icmpmask
;
struct sockaddr_in
*dst_in
=
(struct sockaddr_in
*)&entry
->rt_dst
;
in_sockmaskof(dst_in
->sin_addr
, &icmpmask
);
netmask
= (struct sockaddr
*)&icmpmask
;
extern struct sockaddr_ns ns_netmask
;
netmask
= (struct sockaddr
*)&ns_netmask
;
error
= rtrequest(req
, &(entry
->rt_dst
), &(entry
->rt_gateway
), netmask
,
entry
->rt_flags
, (struct rtentry
**)0);
/* rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL),
&(entry->rt_dst), &(entry->rt_gateway),
netmask, SA(0), entry->rt_flags, error); */
ifa_ifwithroute(flags
, dst
, gateway
)
struct sockaddr
*dst
, *gateway
;
register struct ifaddr
*ifa
;
if ((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.
ifa
= ifa_ifwithdstaddr(dst
);
ifa
= ifa_ifwithaddr(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(gateway
);
ifa
= ifa_ifwithnet(gateway
);
struct rtentry
*rt
= rtalloc1(dst
, 0);
if ((ifa
= rt
->rt_ifa
) == 0)
if (ifa
->ifa_addr
->sa_family
!= dst
->sa_family
) {
struct ifaddr
*oifa
= ifa
, *ifaof_ifpforaddr();
ifa
= ifaof_ifpforaddr(dst
, ifa
->ifa_ifp
);
#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
rtrequest(req
, dst
, gateway
, netmask
, flags
, ret_nrt
)
struct sockaddr
*dst
, *gateway
, *netmask
;
struct rtentry
**ret_nrt
;
int s
= splnet(); int error
= 0;
register struct rtentry
*rt
;
register struct radix_node
*rn
;
register struct radix_node_head
*rnh
;
struct ifaddr
*ifa
, *ifa_ifwithdstaddr();
#define senderr(x) { error = x ; goto bad; }
if ((rnh
= rt_tables
[dst
->sa_family
]) == 0)
if ((rn
= rnh
->rnh_delete((caddr_t
)dst
, (caddr_t
)netmask
,
if (rn
->rn_flags
& (RNF_ACTIVE
| RNF_ROOT
))
panic ("rtrequest delete");
rt
= (struct rtentry
*)rn
;
rt
= rt
->rt_gwroute
; RTFREE(rt
);
(rt
= (struct rtentry
*)rn
)->rt_gwroute
= 0;
if ((ifa
= rt
->rt_ifa
) && ifa
->ifa_rtrequest
)
ifa
->ifa_rtrequest(RTM_DELETE
, rt
, SA(0));
else if (rt
->rt_refcnt
<= 0) {
if (ret_nrt
== 0 || (rt
= *ret_nrt
) == 0)
flags
= rt
->rt_flags
& ~RTF_CLONING
;
gateway
= rt
->rt_gateway
;
if ((netmask
= rt
->rt_genmask
) == 0)
if ((ifa
= ifa_ifwithroute(flags
, dst
, gateway
)) == 0)
R_Malloc(rt
, struct rtentry
*, sizeof(*rt
));
rt
->rt_flags
= RTF_UP
| flags
;
if (rt_setgate(rt
, dst
, gateway
)) {
rt_maskedcopy(dst
, ndst
, netmask
);
Bcopy(dst
, ndst
, dst
->sa_len
);
rn
= rnh
->rnh_add((caddr_t
)ndst
, (caddr_t
)netmask
,
rnh
->rnh_treetop
, rt
->rt_nodes
);
rt
->rt_ifp
= ifa
->ifa_ifp
;
rt
->rt_rmx
= (*ret_nrt
)->rt_rmx
; /* copy metrics */
ifa
->ifa_rtrequest(req
, rt
, SA(ret_nrt
? *ret_nrt
: 0));
rt_setgate(rt0
, dst
, gate
)
struct sockaddr
*dst
, *gate
;
int dlen
= ROUNDUP(dst
->sa_len
), glen
= ROUNDUP(gate
->sa_len
);
register struct rtentry
*rt
= rt0
;
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
);
rt
->rt_nodes
->rn_key
= new;
new = rt
->rt_nodes
->rn_key
;
Bcopy(gate
, (rt
->rt_gateway
= (struct sockaddr
*)(new + dlen
)), glen
);
rt
= rt
->rt_gwroute
; RTFREE(rt
);
rt
= rt0
; rt
->rt_gwroute
= 0;
if (rt
->rt_flags
& RTF_GATEWAY
) {
rt
->rt_gwroute
= rtalloc1(gate
, 1);
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 */
*cp2
++ = *cp1
++ & *cp3
++;
bzero((caddr_t
)cp2
, (unsigned)(cplim2
- cp2
));
* Set up a routing table entry, normally
register struct ifaddr
*ifa
;
register struct rtentry
*rt
;
register struct sockaddr
*dst
;
register struct sockaddr
*deldst
;
dst
= flags
& RTF_HOST
? ifa
->ifa_dstaddr
: ifa
->ifa_addr
;
if ((flags
& RTF_HOST
) == 0 && ifa
->ifa_netmask
) {
m
= m_get(M_WAIT
, MT_SONAME
);
deldst
= mtod(m
, struct sockaddr
*);
rt_maskedcopy(dst
, deldst
, ifa
->ifa_netmask
);
if (rt
= rtalloc1(dst
, 0)) {
return (flags
& RTF_HOST
? EHOSTUNREACH
error
= rtrequest(cmd
, dst
, ifa
->ifa_addr
, ifa
->ifa_netmask
,
flags
| ifa
->ifa_flags
, &nrt
);
if (cmd
== RTM_DELETE
&& error
== 0 && (rt
= nrt
)) {
rt_newaddrmsg(cmd
, ifa
, error
, nrt
);
if (rt
->rt_refcnt
<= 0) {
if (cmd
== RTM_ADD
&& error
== 0 && (rt
= nrt
)) {
printf("rtinit: wrong ifa (%x) was (%x)\n", ifa
,
if (rt
->rt_ifa
->ifa_rtrequest
)
rt
->rt_ifa
->ifa_rtrequest(RTM_DELETE
, rt
, SA(0));
rt
->rt_ifp
= ifa
->ifa_ifp
;
ifa
->ifa_rtrequest(RTM_ADD
, rt
, SA(0));
rt_newaddrmsg(cmd
, ifa
, error
, nrt
);