* 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.13 (Berkeley) %G%
#include "../netinet/in.h"
#include "../netinet/in_var.h"
#include "machine/mtpr.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
;
struct radix_node
*rn_match(), *rn_delete(), *rn_addroute();
rn_inithead(&ns_rnhead
, 16, AF_NS
) &&
rn_inithead(&in_rnhead
, 32, AF_INET
))
* 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 radix_node_head
*rnh
;
register struct radix_node
*rn
;
register struct rtentry
*rt
= 0;
u_char af
= dst
->sa_family
;
for (rnh
= radix_node_head
; rnh
&& (af
!= rnh
->rnh_af
); )
if (rnh
&& rnh
->rnh_treetop
&&
(rn
= rn_match((caddr_t
)dst
, rnh
->rnh_treetop
)) &&
((rn
->rn_flags
& RNF_ROOT
) == 0)) {
rt
= (struct rtentry
*)rn
;
if (report
&& (rt
->rt_flags
& RTF_CLONING
)) {
struct rtentry
*newrt
= 0;
int flags
= rt
->rt_flags
& ~RTF_CLONING
;
if (rt
->rt_genmask
== 0) flags
|= RTF_HOST
;
(void) rtrequest(RTM_ADD
, dst
, rt
->rt_gateway
,
rt
->rt_genmask
, flags
, &newrt
);
rt_missmsg(RTM_MISS
, dst
, (struct sockaddr
*)0,
(struct sockaddr
*)0, (struct sockaddr
*)0, 0, 0);
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
))
free((caddr_t
)rt
, 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
, 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
,
(struct sockaddr
*)0, flags
,
stat
= &rtstat
.rts_dynamic
;
* Smash the current notion of the gateway to
* this destination. Should check about netmask!!!
if (gateway
->sa_len
<= rt
->rt_gateway
->sa_len
) {
Bcopy(gateway
, rt
->rt_gateway
, gateway
->sa_len
);
rt
->rt_flags
|= RTF_MODIFIED
;
stat
= &rtstat
.rts_newgateway
;
rtstat
.rts_badredirect
++;
rt_missmsg(RTM_REDIRECT
, dst
, gateway
, netmask
, src
, flags
, error
);
* Routing table ioctl interface.
register struct ortentry
*entry
= (struct ortentry
*)data
;
struct sockaddr
*netmask
= 0;
else if (req
== SIOCDELRT
)
if (error
= suser(u
.u_cred
, &u
.u_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
, (struct sockaddr
*)0, entry
->rt_flags
, error
);
#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
rtrequest(req
, dst
, gateway
, netmask
, flags
, ret_nrt
)
struct sockaddr
*dst
, *gateway
, *netmask
;
struct rtentry
**ret_nrt
;
int s
= splnet(), len
, error
= 0;
register struct rtentry
*rt
;
register struct radix_node
*rn
;
register struct radix_node_head
*rnh
;
struct ifaddr
*ifa
, *ifa_ifwithdstaddr();
u_char af
= dst
->sa_family
;
#define senderr(x) { error = x ; goto bad; }
for (rnh
= radix_node_head
; rnh
&& (af
!= rnh
->rnh_af
); )
if (ret_nrt
&& (rt
= *ret_nrt
)) {
if ((rn
= rn_delete((caddr_t
)dst
, (caddr_t
)netmask
,
if (rn
->rn_flags
& (RNF_ACTIVE
| RNF_ROOT
))
panic ("rtrequest delete");
rt
= (struct rtentry
*)rn
;
if ((ifa
= rt
->rt_ifa
) && ifa
->ifa_rtrequest
)
ifa
->ifa_rtrequest(RTM_DELETE
, rt
, (struct sockaddr
*)0);
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
);
if (ifa
== 0 && req
== RTM_ADD
)
len
= sizeof (*rt
) + ROUNDUP(gateway
->sa_len
)
+ ROUNDUP(dst
->sa_len
) + ROUNDUP(ifa
->ifa_llinfolen
);
R_Malloc(rt
, struct rtentry
*, len
);
ndst
= (struct sockaddr
*)(rt
+ 1);
rt_maskedcopy(dst
, ndst
, netmask
);
Bcopy(dst
, ndst
, dst
->sa_len
);
rn
= rn_addroute((caddr_t
)ndst
, (caddr_t
)netmask
,
rnh
->rnh_treetop
, rt
->rt_nodes
);
free((caddr_t
)rt
, M_RTABLE
);
rt
->rt_ifp
= ifa
->ifa_ifp
;
rt
->rt_flags
= RTF_UP
| flags
;
rn
->rn_key
= (caddr_t
) ndst
; /* == rt_dst */
rt
->rt_gateway
= (struct sockaddr
*)
(rn
->rn_key
+ ROUNDUP(dst
->sa_len
));
Bcopy(gateway
, rt
->rt_gateway
, gateway
->sa_len
);
rt
->rt_llinfo
= ROUNDUP(gateway
->sa_len
) + (caddr_t
)gateway
;
ifa
->ifa_rtrequest(RTM_ADD
, rt
, (struct sockaddr
*)0);
rt_maskedcopy(src
, dst
, netmask
)
struct sockaddr
*src
, *dst
, *netmask
;
register caddr_t cp1
= (caddr_t
)src
, cp2
= (caddr_t
)dst
,
cp3
= 2 + (caddr_t
)netmask
, cplim
= cp1
+ *(u_char
*)cp1
;
*cp2
++ = *cp1
++; *cp2
++ = *cp1
++; /* copies sa_len & sa_family */
*cp2
++ = *cp1
++ & *cp3
++;
* Set up a routing table entry, normally
register struct ifaddr
*ifa
;
return rtrequest(cmd
, ifa
->ifa_dstaddr
, ifa
->ifa_addr
,
ifa
->ifa_netmask
, flags
| ifa
->ifa_flags
, &ifa
->ifa_rt
);