static char sccsid
[] = "@(#)routed.c 4.25 %G%";
* Routing Table Management Daemon
/* casts to keep lint happy */
#define insque(q,p) _insque((caddr_t)q,(caddr_t)p)
#define remque(q) _remque((caddr_t)q)
(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
#define min(a,b) ((a)>(b)?(b):(a))
struct sockaddr_in routingaddr
= { AF_INET
};
struct sockaddr_in noroutingaddr
= { AF_INET
};
int snoroute
; /* socket with no routing */
int supplier
= -1; /* process should supply updates */
int install
= 1; /* if 1 call kernel */
int lookforinterfaces
= 1;
int externalinterfaces
= 0; /* # of remote and local interfaces */
int timeval
= -TIMER_RATE
;
#define tprintf if (trace) printf
char packet
[MAXPACKETSIZE
+1];
struct rip
*msg
= (struct rip
*)packet
;
struct in_addr
inet_makeaddr();
struct interface
*if_ifwithaddr(), *if_ifwithnet();
extern char *malloc(), *sys_errlist
[];
extern int errno
, exit();
for (cc
= 0; cc
< 10; cc
++)
{ int t
= open("/dev/tty", 2);
ioctl(t
, TIOCNOTTY
, (char *)0);
ftrace
= fopen("/etc/routerlog", "w");
* We use two sockets. One for which outgoing
* packets are routed and for which they're not.
* The latter allows us to delete routing table
* entries in the kernel for network interfaces
* attached to our host which we believe are down
* while still polling it to see when/if it comes
* back up. With the new ipc interface we'll be
* able to specify ``don't route'' as an option
* to send, but until then we utilize a second port.
sp
= getservbyname("router", "udp");
fprintf(stderr
, "routed: udp/router: unknown service\n");
routingaddr
.sin_port
= htons(sp
->s_port
);
noroutingaddr
.sin_port
= htons(sp
->s_port
+ 1);
s
= socket(SOCK_DGRAM
, 0, &routingaddr
, 0);
snoroute
= socket(SOCK_DGRAM
, 0, &noroutingaddr
, SO_DONTROUTE
);
while (argc
> 0 && **argv
== '-') {
if (!strcmp(*argv
, "-s") == 0) {
if (!strcmp(*argv
, "-q") == 0) {
fprintf(stderr
, "usage: routed [ -sq ]\n");
* Collect an initial view of the world by
* snooping in the kernel and the gateway kludge
* file. Then, send a request packet on all
* directly connected networks to find out what
msg
->rip_cmd
= RIPCMD_REQUEST
;
msg
->rip_nets
[0].rip_dst
.sa_family
= AF_UNSPEC
;
msg
->rip_nets
[0].rip_metric
= HOPCNT_INFINITY
;
cc
= receive(s
, &from
, packet
, sizeof (packet
));
if (cc
< 0 && errno
!= EINTR
)
register struct rthash
*rh
;
for (rh
= nethash
; rh
< &nethash
[ROUTEHASHSIZ
]; rh
++)
rh
->rt_forw
= rh
->rt_back
= (struct rt_entry
*)rh
;
for (rh
= hosthash
; rh
< &hosthash
[ROUTEHASHSIZ
]; rh
++)
rh
->rt_forw
= rh
->rt_back
= (struct rt_entry
*)rh
;
* Probe the kernel through /dev/kmem to find the network
* interfaces which have configured themselves. If the
* interface is present but not yet up (for example an
* ARPANET IMP), set the lookforinterfaces flag so we'll
* come back later and look again.
if (nl
[N_IFNET
].n_value
== 0) {
printf("ifnet: not in namelist\n");
kmem
= open("/dev/kmem", 0);
if (lseek(kmem
, (long)nl
[N_IFNET
].n_value
, 0) == -1 ||
read(kmem
, (char *)&next
, sizeof (next
)) != sizeof (next
)) {
printf("ifnet: error reading kmem\n");
if (lseek(kmem
, (long)next
, 0) == -1 ||
read(kmem
, (char *)&ifs
, sizeof (ifs
)) != sizeof (ifs
)) {
if ((ifs
.if_flags
& IFF_UP
) == 0) {
/* already known to us? */
if (if_ifwithaddr(&ifs
.if_addr
))
/* argh, this'll have to change sometime */
if (ifs
.if_addr
.sa_family
!= AF_INET
)
/* no one cares about software loopback interfaces */
if (ifs
.if_net
== LOOPBACKNET
)
ifp
= (struct interface
*)malloc(sizeof (struct interface
));
printf("routed: out of memory\n");
* Count the # of directly connected networks
* and point to point links which aren't looped
* back to ourself. This is used below to
* decide if we should be a routing ``supplier''.
if ((ifs
.if_flags
& IFF_POINTOPOINT
) == 0 ||
if_ifwithaddr(&ifs
.if_dstaddr
) == 0)
ifp
->int_addr
= ifs
.if_addr
;
ifp
->int_flags
= ifs
.if_flags
| IFF_INTERFACE
;
/* this works because broadaddr overlaps dstaddr */
ifp
->int_broadaddr
= ifs
.if_broadaddr
;
ifp
->int_net
= ifs
.if_net
;
if (externalinterfaces
> 1 && supplier
< 0)
close(kmem
), close(s
), close(snoroute
);
execv("/etc/routed", argv0
);
if (ifp
->int_flags
& IFF_POINTOPOINT
)
bzero((char *)&net
, sizeof (net
));
net
.sin_family
= AF_INET
;
net
.sin_addr
= inet_makeaddr(ifp
->int_net
, INADDR_ANY
);
dst
= (struct sockaddr
*)&net
;
rtadd(dst
, &ifp
->int_addr
, ifp
->int_metric
,
ifp
->int_flags
& (IFF_INTERFACE
|IFF_PASSIVE
|IFF_REMOTE
));
* As a concession to the ARPANET we read a list of gateways
* from /etc/gateways and add them to our tables. This file
* exists at each ARPANET gateway and indicates a set of ``remote''
* gateways (i.e. a gateway which we can't immediately determine
* if it's present or not as we can do for those directly connected
* at the hardware level). If a gateway is marked ``passive''
* in the file, then we assume it doesn't have a routing process
* of our design and simply assume it's always present. Those
* not marked passive are treated as if they were directly
* connected -- they're added into the interface list so we'll
* send them routing updates.
struct sockaddr_in dst
, gate
;
char *type
, *dname
, *gname
, *qual
, buf
[BUFSIZ
];
fp
= fopen("/etc/gateways", "r");
gname
= buf
+ ((BUFSIZ
- 64) / 3);
type
= buf
+ (((BUFSIZ
- 64) * 2) / 3);
bzero((char *)&dst
, sizeof (dst
));
bzero((char *)&gate
, sizeof (gate
));
dst
.sin_family
= gate
.sin_family
= AF_INET
;
/* format: {net | host} XX gateway XX metric DD [passive]\n */
fscanf((fp), "%s %s gateway %s metric %d %s\n", \
type, dname, gname, &metric, qual)
if (readentry(fp
) == EOF
)
if (strcmp(type
, "net") == 0) {
net
= getnetbyname(dname
);
if (net
== 0 || net
->n_addrtype
!= AF_INET
)
dst
.sin_addr
= inet_makeaddr(net
->n_net
, INADDR_ANY
);
} else if (strcmp(type
, "host") == 0) {
host
= gethostbyname(dname
);
bcopy(host
->h_addr
, &dst
.sin_addr
, host
->h_length
);
host
= gethostbyname(gname
);
bcopy(host
->h_addr
, &gate
.sin_addr
, host
->h_length
);
ifp
= (struct interface
*)malloc(sizeof (*ifp
));
bzero((char *)ifp
, sizeof (*ifp
));
ifp
->int_flags
= IFF_REMOTE
;
/* can't identify broadcast capability */
ifp
->int_net
= inet_netof(dst
.sin_addr
);
if (strcmp(type
, "host") == 0) {
ifp
->int_flags
|= IFF_POINTOPOINT
;
ifp
->int_dstaddr
= *((struct sockaddr
*)&dst
);
if (strcmp(qual
, "passive") == 0)
ifp
->int_flags
|= IFF_PASSIVE
;
/* assume no duplicate entries */
ifp
->int_addr
= *((struct sockaddr
*)&gate
);
ifp
->int_metric
= metric
;
* Timer routine. Performs routing information supply
* duties and manages timers on routing table entries.
register struct rthash
*rh
;
register struct rt_entry
*rt
;
struct rthash
*base
= hosthash
;
int doinghost
= 1, timetobroadcast
;
if (lookforinterfaces
&& (timeval
% CHECK_INTERVAL
) == 0)
timetobroadcast
= supplier
&& (timeval
% SUPPLY_INTERVAL
) == 0;
tprintf(">>> time %d >>>\n", timeval
);
for (rh
= base
; rh
< &base
[ROUTEHASHSIZ
]; rh
++) {
for (; rt
!= (struct rt_entry
*)rh
; rt
= rt
->rt_forw
) {
* We don't advance time on a routing entry for
* a passive gateway or that for our only interface.
* The latter is excused because we don't act as
* a routing information supplier and hence would
* time it out. This is fair as if it's down
* we're cut off from the world anyway and it's
* not likely we'll grow any new hardware in
if (!(rt
->rt_state
& RTS_PASSIVE
) &&
(supplier
|| !(rt
->rt_state
& RTS_INTERFACE
)))
rt
->rt_timer
+= TIMER_RATE
;
if (rt
->rt_timer
>= EXPIRE_TIME
)
rt
->rt_metric
= HOPCNT_INFINITY
;
if (rt
->rt_timer
>= GARBAGE_TIME
) {
if (rt
->rt_state
& RTS_CHANGED
) {
rt
->rt_state
&= ~RTS_CHANGED
;
/* don't send extraneous packets */
if (!supplier
|| timetobroadcast
)
msg
->rip_cmd
= RIPCMD_RESPONSE
;
msg
->rip_nets
[0].rip_dst
= rt
->rt_dst
;
msg
->rip_nets
[0].rip_metric
=
min(rt
->rt_metric
+1, HOPCNT_INFINITY
);
tprintf("<<< time %d <<<\n", timeval
);
register struct interface
*ifp
;
register struct sockaddr
*dst
;
for (ifp
= ifnet
; ifp
; ifp
= ifp
->int_next
) {
if (ifp
->int_flags
& IFF_PASSIVE
)
dst
= ifp
->int_flags
& IFF_BROADCAST
? &ifp
->int_broadaddr
:
ifp
->int_flags
& IFF_POINTOPOINT
? &ifp
->int_dstaddr
:
(*f
)(dst
, ifp
->int_flags
& IFF_INTERFACE
);
(*afswitch
[dst
->sa_family
].af_output
)(s
, dst
, sizeof (struct rip
));
* Supply dst with the contents of the routing tables.
* If this won't fit in one packet, chop it up into several.
register struct rt_entry
*rt
;
struct netinfo
*n
= msg
->rip_nets
;
register struct rthash
*rh
;
struct rthash
*base
= hosthash
;
int (*output
)() = afswitch
[dst
->sa_family
].af_output
;
int sto
= dontroute
? snoroute
: s
;
msg
->rip_cmd
= RIPCMD_RESPONSE
;
for (rh
= base
; rh
< &base
[ROUTEHASHSIZ
]; rh
++)
for (rt
= rh
->rt_forw
; rt
!= (struct rt_entry
*)rh
; rt
= rt
->rt_forw
) {
size
= (char *)n
- packet
;
if (size
> MAXPACKETSIZE
- sizeof (struct netinfo
)) {
(*output
)(sto
, dst
, size
);
n
->rip_metric
= min(rt
->rt_metric
+ 1, HOPCNT_INFINITY
);
(*output
)(sto
, dst
, (char *)n
- packet
);
* Handle an incoming routing packet.
if (msg
->rip_cmd
< RIPCMD_MAX
)
printf("%s from %x\n", ripcmds
[msg
->rip_cmd
],
((struct sockaddr_in
*)from
)->sin_addr
);
printf("%x from %x\n", msg
->rip_cmd
,
((struct sockaddr_in
*)from
)->sin_addr
);
if (from
->sa_family
>= AF_MAX
)
afp
= &afswitch
[from
->sa_family
];
size
-= 4 * sizeof (char);
if (size
< sizeof (struct netinfo
))
size
-= sizeof (struct netinfo
);
* A single entry with sa_family == AF_UNSPEC and
* metric ``infinity'' means ``all routes''.
if (n
->rip_dst
.sa_family
== AF_UNSPEC
&&
n
->rip_metric
== HOPCNT_INFINITY
&& size
== 0) {
rt
= rtlookup(&n
->rip_dst
);
n
->rip_metric
= rt
== 0 ? HOPCNT_INFINITY
:
min(rt
->rt_metric
+1, HOPCNT_INFINITY
);
n
++, newsize
+= sizeof (struct netinfo
);
msg
->rip_cmd
= RIPCMD_RESPONSE
;
(*afp
->af_output
)(s
, from
, newsize
);
if ((*afp
->af_portcheck
)(from
) == 0)
ftrace
= fopen(msg
->rip_tracefile
, "a");
(void) dup2(fileno(ftrace
), 1);
(void) dup2(fileno(ftrace
), 2);
printf("*** Tracing turned on at %.24s ***\n", ctime(&t
));
/* verify message came from a priviledged port */
if ((*afp
->af_portcheck
)(from
) == 0)
printf("*** Tracing turned off at %.24s ***\n", ctime(&t
));
fflush(stdout
), fflush(stderr
);
(void) close(1), (void) close(2);
/* verify message came from a router */
if ((*afp
->af_portmatch
)(from
) == 0)
/* are we talking to ourselves? */
ifp
= if_ifwithaddr(from
);
size
-= 4 * sizeof (char);
for (; size
> 0; size
-= sizeof (struct netinfo
), n
++) {
if (size
< sizeof (struct netinfo
))
if (n
->rip_metric
>= HOPCNT_INFINITY
)
tprintf("dst %x hc %d...",
((struct sockaddr_in
*)&n
->rip_dst
)->sin_addr
,
rt
= rtlookup(&n
->rip_dst
);
rtadd(&n
->rip_dst
, from
, n
->rip_metric
, 0);
tprintf("ours: gate %x hc %d timer %d\n",
((struct sockaddr_in
*)&rt
->rt_router
)->sin_addr
,
rt
->rt_metric
, rt
->rt_timer
);
* Update if from gateway, shorter, or getting
if (equal(from
, &rt
->rt_router
) ||
n
->rip_metric
< rt
->rt_metric
||
(rt
->rt_timer
> (EXPIRE_TIME
/2) &&
rt
->rt_metric
== n
->rip_metric
)) {
rtchange(rt
, from
, n
->rip_metric
);
tprintf("bad packet, cmd=%x\n", msg
->rip_cmd
);
* Lookup dst in the tables for an exact match.
register struct rt_entry
*rt
;
register struct rthash
*rh
;
if (dst
->sa_family
>= AF_MAX
)
(*afswitch
[dst
->sa_family
].af_hash
)(dst
, &h
);
rh
= &hosthash
[hash
% ROUTEHASHSIZ
];
for (rt
= rh
->rt_forw
; rt
!= (struct rt_entry
*)rh
; rt
= rt
->rt_forw
) {
if (equal(&rt
->rt_dst
, dst
))
rh
= &nethash
[hash
% ROUTEHASHSIZ
];
* Find a route to dst as the kernel would.
register struct rt_entry
*rt
;
register struct rthash
*rh
;
int doinghost
= 1, (*match
)();
(*afswitch
[af
].af_hash
)(dst
, &h
);
rh
= &hosthash
[hash
% ROUTEHASHSIZ
];
for (rt
= rh
->rt_forw
; rt
!= (struct rt_entry
*)rh
; rt
= rt
->rt_forw
) {
if (equal(&rt
->rt_dst
, dst
))
if (rt
->rt_dst
.sa_family
== af
&&
(*match
)(&rt
->rt_dst
, dst
))
rh
= &nethash
[hash
% ROUTEHASHSIZ
];
match
= afswitch
[af
].af_netmatch
;
rtadd(dst
, gate
, metric
, state
)
struct sockaddr
*dst
, *gate
;
register struct rt_entry
*rt
;
int af
= dst
->sa_family
, flags
, hash
;
(*afswitch
[af
].af_hash
)(dst
, &h
);
flags
= (*afswitch
[af
].af_checkhost
)(dst
) ? RTF_HOST
: 0;
rh
= &hosthash
[hash
% ROUTEHASHSIZ
];
rh
= &nethash
[hash
% ROUTEHASHSIZ
];
rt
= (struct rt_entry
*)malloc(sizeof (*rt
));
rt
->rt_flags
= RTF_UP
| flags
;
rt
->rt_state
= state
| RTS_CHANGED
;
rt
->rt_ifp
= if_ifwithnet(&rt
->rt_router
);
rt
->rt_flags
|= RTF_GATEWAY
;
if (install
&& ioctl(s
, SIOCADDRT
, (char *)&rt
->rt_rt
) < 0)
tprintf("SIOCADDRT: %s\n", sys_errlist
[errno
]);
rtchange(rt
, gate
, metric
)
int doioctl
= 0, metricchanged
= 0;
if (!equal(&rt
->rt_router
, gate
))
if (metric
!= rt
->rt_metric
) {
if (doioctl
|| metricchanged
) {
rt
->rt_state
|= RTS_CHANGED
;
if (ioctl(s
, SIOCADDRT
, (char *)&rt
->rt_rt
) < 0)
tprintf("SIOCADDRT: %s\n", sys_errlist
[errno
]);
if (ioctl(s
, SIOCDELRT
, (char *)&oldroute
) < 0)
tprintf("SIOCDELRT: %s\n", sys_errlist
[errno
]);
if (install
&& ioctl(s
, SIOCDELRT
, (char *)&rt
->rt_rt
))
tprintf("SIOCDELRT: %s\n", sys_errlist
[errno
]);
struct sockaddr_in
*dst
, *gate
;
{ RTF_GATEWAY
, "GATEWAY" },
{ RTS_PASSIVE
, "PASSIVE" },
{ RTS_REMOTE
, "REMOTE" },
{ RTS_INTERFACE
,"INTERFACE" },
{ RTS_CHANGED
, "CHANGED" },
printf("%s ", operation
);
dst
= (struct sockaddr_in
*)&rt
->rt_dst
;
gate
= (struct sockaddr_in
*)&rt
->rt_router
;
printf("dst %x, router %x, metric %d, flags", dst
->sin_addr
,
gate
->sin_addr
, rt
->rt_metric
);
for (first
= 1, p
= flagbits
; p
->t_bits
> 0; p
++) {
if ((rt
->rt_flags
& p
->t_bits
) == 0)
for (first
= 1, p
= statebits
; p
->t_bits
> 0; p
++) {
if ((rt
->rt_state
& p
->t_bits
) == 0)
register struct interface
*ifp
;
(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
for (ifp
= ifnet
; ifp
; ifp
= ifp
->int_next
) {
if (ifp
->int_flags
& IFF_REMOTE
)
if (ifp
->int_addr
.sa_family
!= addr
->sa_family
)
if (same(&ifp
->int_addr
, addr
))
if ((ifp
->int_flags
& IFF_BROADCAST
) &&
same(&ifp
->int_broadaddr
, addr
))
register struct sockaddr
*addr
;
register struct interface
*ifp
;
register int af
= addr
->sa_family
;
register int (*netmatch
)();
netmatch
= afswitch
[af
].af_netmatch
;
for (ifp
= ifnet
; ifp
; ifp
= ifp
->int_next
) {
if (ifp
->int_flags
& IFF_REMOTE
)
if (af
!= ifp
->int_addr
.sa_family
)
if ((*netmatch
)(addr
, &ifp
->int_addr
))