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