xref: /freebsd/sys/netinet/in_rmx.c (revision a316b26e50bbed7cf655fbba726ab87d8ab7599d)
1 /*
2  * Copyright 1994, Massachusetts Institute of Technology.  All Rights Reserved.
3  *
4  * You may copy this file verbatim until I find the official
5  * Institute boilerplate.
6  *
7  * $Id: in_rmx.c,v 1.6 1994/12/13 22:32:45 wollman Exp $
8  */
9 
10 /*
11  * This code does two things necessary for the enhanced TCP metrics to
12  * function in a useful manner:
13  *  1) It marks all non-host routes as `cloning', thus ensuring that
14  *     every actual reference to such a route actually gets turned
15  *     into a reference to a host route to the specific destination
16  *     requested.
17  *  2) When such routes lose all their references, it arranges for them
18  *     to be deleted in some random collection of circumstances, so that
19  *     a large quantity of stale routing data is not kept in kernel memory
20  *     indefinitely.  See in_rtqtimo() below for the exact mechanism.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/kernel.h>
26 #include <sys/queue.h>
27 #include <sys/socket.h>
28 #include <sys/socketvar.h>
29 #include <sys/mbuf.h>
30 #include <sys/syslog.h>
31 
32 #include <net/if.h>
33 #include <net/route.h>
34 #include <netinet/in.h>
35 #include <netinet/in_systm.h>
36 #include <netinet/in_var.h>
37 
38 #define RTPRF_OURS		RTF_PROTO3	/* set on routes we manage */
39 
40 /*
41  * Do what we need to do when inserting a route.
42  */
43 static struct radix_node *
44 in_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
45 	    struct radix_node *treenodes)
46 {
47 	struct rtentry *rt = (struct rtentry *)treenodes;
48 	struct in_rtq *inr;
49 
50 	/*
51 	 * For IP, all non-host routes are automatically cloning.
52 	 */
53 	if(!(rt->rt_flags & (RTF_HOST | RTF_CLONING)))
54 		rt->rt_flags |= RTF_PRCLONING;
55 
56 	return rn_addroute(v_arg, n_arg, head, treenodes);
57 }
58 
59 /*
60  * This code is the inverse of in_clsroute: on first reference, if we
61  * were managing the route, stop doing so and set the expiration timer
62  * back off again.
63  */
64 static struct radix_node *
65 in_matroute(void *v_arg, struct radix_node_head *head)
66 {
67 	struct radix_node *rn = rn_match(v_arg, head);
68 	struct rtentry *rt = (struct rtentry *)rn;
69 
70 	if(rt && rt->rt_refcnt == 0) { /* this is first reference */
71 		if(rt->rt_flags & RTPRF_OURS) {
72 			rt->rt_flags &= ~RTPRF_OURS;
73 			rt->rt_rmx.rmx_expire = 0;
74 		}
75 	}
76 	return rn;
77 }
78 
79 #define RTQ_REALLYOLD	4*60*60	/* four hours is ``really old'' */
80 int rtq_reallyold = RTQ_REALLYOLD;
81 
82 /*
83  * On last reference drop, add the route to the queue so that it can be
84  * timed out.
85  */
86 static void
87 in_clsroute(struct radix_node *rn, struct radix_node_head *head)
88 {
89 	struct rtentry *rt = (struct rtentry *)rn;
90 	struct in_rtq *inr;
91 
92 	if((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST)
93 		return;
94 
95 	if((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS))
96 	   != RTF_WASCLONED)
97 		return;
98 
99 	rt->rt_flags |= RTPRF_OURS;
100 	rt->rt_rmx.rmx_expire = time.tv_sec + rtq_reallyold;
101 }
102 
103 #define RTQ_TIMEOUT	60	/* run once a minute */
104 int rtq_timeout = RTQ_TIMEOUT;
105 
106 struct rtqk_arg {
107 	struct radix_node_head *rnh;
108 	int draining;
109 	int killed;
110 	int found;
111 	time_t nextstop;
112 };
113 
114 /*
115  * Get rid of old routes.  When draining, this deletes everything, even when
116  * the timeout is not expired yet.
117  */
118 static int
119 in_rtqkill(struct radix_node *rn, void *rock)
120 {
121 	struct rtqk_arg *ap = rock;
122 	struct radix_node_head *rnh = ap->rnh;
123 	struct rtentry *rt = (struct rtentry *)rn;
124 	int err;
125 
126 	if(rt->rt_flags & RTPRF_OURS) {
127 		ap->found++;
128 
129 		if(ap->draining || rt->rt_rmx.rmx_expire <= time.tv_sec) {
130 			if(rt->rt_refcnt > 0)
131 				panic("rtqkill route really not free\n");
132 
133 			err = rtrequest(RTM_DELETE,
134 					(struct sockaddr *)rt_key(rt),
135 					rt->rt_gateway, rt_mask(rt),
136 					rt->rt_flags, 0);
137 			if(err) {
138 				log(LOG_WARNING, "in_rtqkill: error %d", err);
139 			} else {
140 				ap->killed++;
141 			}
142 		} else {
143 			ap->nextstop = lmin(ap->nextstop,
144 					    rt->rt_rmx.rmx_expire);
145 		}
146 	}
147 
148 	return 0;
149 }
150 
151 static void
152 in_rtqtimo(void *rock)
153 {
154 	struct radix_node_head *rnh = rock;
155 	struct rtqk_arg arg;
156 	struct timeval atv;
157 	int s;
158 
159 	arg.found = arg.killed = 0;
160 	arg.rnh = rnh;
161 	arg.nextstop = time.tv_sec + 10*rtq_timeout;
162 	arg.draining = 0;
163 	s = splnet();
164 	rnh->rnh_walktree(rnh, in_rtqkill, &arg);
165 	splx(s);
166 	atv.tv_usec = 0;
167 	atv.tv_sec = arg.nextstop;
168 	timeout(in_rtqtimo, rock, hzto(&atv));
169 }
170 
171 void
172 in_rtqdrain(void)
173 {
174 	struct radix_node_head *rnh = rt_tables[AF_INET];
175 	struct rtqk_arg arg;
176 	int s;
177 	arg.found = arg.killed = 0;
178 	arg.rnh = rnh;
179 	arg.nextstop = 0;
180 	arg.draining = 1;
181 	s = splnet();
182 	rnh->rnh_walktree(rnh, in_rtqkill, &arg);
183 	splx(s);
184 }
185 
186 /*
187  * Initialize our routing tree.
188  */
189 int
190 in_inithead(void **head, int off)
191 {
192 	struct radix_node_head *rnh;
193 
194 	if(!rn_inithead(head, off))
195 		return 0;
196 
197 	if(head != (void **)&rt_tables[AF_INET]) /* BOGUS! */
198 		return 1;	/* only do this for the real routing table */
199 
200 	rnh = *head;
201 	rnh->rnh_addaddr = in_addroute;
202 	rnh->rnh_matchaddr = in_matroute;
203 	rnh->rnh_close = in_clsroute;
204 	in_rtqtimo(rnh);	/* kick off timeout first time */
205 	return 1;
206 }
207 
208