xref: /original-bsd/sys/net/rtsock.c (revision 92ab646d)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)rtsock.c	7.13 (Berkeley) 07/24/90
8  */
9 
10 #include "param.h"
11 #include "mbuf.h"
12 #include "user.h"
13 #include "proc.h"
14 #include "socket.h"
15 #include "socketvar.h"
16 #include "domain.h"
17 #include "protosw.h"
18 #include "errno.h"
19 
20 #include "af.h"
21 #include "if.h"
22 #include "route.h"
23 #include "raw_cb.h"
24 
25 #include "machine/mtpr.h"
26 
27 struct sockaddr route_dst = { 2, PF_ROUTE, };
28 struct sockaddr route_src = { 2, PF_ROUTE, };
29 struct sockproto route_proto = { PF_ROUTE, };
30 
31 /*ARGSUSED*/
32 route_usrreq(so, req, m, nam, control)
33 	register struct socket *so;
34 	int req;
35 	struct mbuf *m, *nam, *control;
36 {
37 	register int error = 0;
38 	register struct rawcb *rp = sotorawcb(so);
39 	int s;
40 	if (req == PRU_ATTACH) {
41 		MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
42 		if (so->so_pcb = (caddr_t)rp)
43 			bzero(so->so_pcb, sizeof(*rp));
44 
45 	}
46 	if (req == PRU_DETACH && rp) {
47 		int af = rp->rcb_proto.sp_protocol;
48 		if (af == AF_INET)
49 			route_cb.ip_count--;
50 		else if (af == AF_NS)
51 			route_cb.ns_count--;
52 		else if (af == AF_ISO)
53 			route_cb.iso_count--;
54 		route_cb.any_count--;
55 	}
56 	s = splnet();
57 	error = raw_usrreq(so, req, m, nam, control);
58 	rp = sotorawcb(so);
59 	if (req == PRU_ATTACH && rp) {
60 		int af = rp->rcb_proto.sp_protocol;
61 		if (error) {
62 			free((caddr_t)rp, M_PCB);
63 			splx(s);
64 			return (error);
65 		}
66 		if (af == AF_INET)
67 			route_cb.ip_count++;
68 		else if (af == AF_NS)
69 			route_cb.ns_count++;
70 		else if (af == AF_ISO)
71 			route_cb.iso_count++;
72 		rp->rcb_faddr = &route_src;
73 		route_cb.any_count++;
74 		soisconnected(so);
75 		so->so_options |= SO_USELOOPBACK;
76 	}
77 	splx(s);
78 	return (error);
79 }
80 #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
81 
82 /*ARGSUSED*/
83 route_output(m, so)
84 	register struct mbuf *m;
85 	struct socket *so;
86 {
87 	register struct rt_msghdr *rtm = 0;
88 	register struct rtentry *rt = 0;
89 	struct rtentry *saved_nrt = 0;
90 	struct sockaddr *dst = 0, *gate = 0, *netmask = 0, *genmask = 0;
91 	struct sockaddr *ifpaddr = 0;
92 	caddr_t cp, lim;
93 	int len, error = 0;
94 	struct ifnet *ifp = 0;
95 	struct	ifaddr *ifa;
96 	extern struct ifaddr *ifaof_ifpforaddr(), *ifa_ifwithroute();
97 
98 #define senderr(e) { error = e; goto flush;}
99 	if (m == 0 || m->m_len < sizeof(long))
100 		return (ENOBUFS);
101 	if ((m = m_pullup(m, sizeof(long))) == 0)
102 		return (ENOBUFS);
103 	if ((m->m_flags & M_PKTHDR) == 0)
104 		panic("route_output");
105 	len = m->m_pkthdr.len;
106 	if (len < sizeof(*rtm) ||
107 	    len != mtod(m, struct rt_msghdr *)->rtm_msglen)
108 		senderr(EINVAL);
109 	R_Malloc(rtm, struct rt_msghdr *, len);
110 	if (rtm == 0)
111 		senderr(ENOBUFS);
112 	m_copydata(m, 0, len, (caddr_t)rtm);
113 	if (rtm->rtm_version != RTM_VERSION)
114 		senderr(EPROTONOSUPPORT);
115 	rtm->rtm_pid = u.u_procp->p_pid;
116 	lim = len + (caddr_t) rtm;
117 	cp = (caddr_t) (rtm + 1);
118 	if (rtm->rtm_addrs & RTA_DST) {
119 		dst = (struct sockaddr *)cp;
120 		cp += ROUNDUP(dst->sa_len);
121 	} else
122 		senderr(EINVAL);
123 	if ((rtm->rtm_addrs & RTA_GATEWAY) && cp < lim)  {
124 		gate = (struct sockaddr *)cp;
125 		cp += ROUNDUP(gate->sa_len);
126 	}
127 	if ((rtm->rtm_addrs & RTA_NETMASK) && cp < lim)  {
128 		netmask = (struct sockaddr *)cp;
129 		if (*cp)
130 			cp += ROUNDUP(netmask->sa_len);
131 		else
132 			cp += sizeof(long);
133 
134 	}
135 	if ((rtm->rtm_addrs & RTA_GENMASK) && cp < lim)  {
136 		struct radix_node *t, *rn_addmask();
137 		genmask = (struct sockaddr *)cp;
138 		if (*cp)
139 			cp += ROUNDUP(netmask->sa_len);
140 		else
141 			cp += sizeof(long);
142 		t = rn_addmask(genmask, 1, 2);
143 		if (t && Bcmp(genmask, t->rn_key, *(u_char *)genmask) == 0)
144 			genmask = (struct sockaddr *)(t->rn_key);
145 		else
146 			senderr(ENOBUFS);
147 	}
148 	if ((rtm->rtm_addrs & RTA_IFP) && cp < lim)  {
149 		ifpaddr = (struct sockaddr *)cp;
150 	}
151 	switch (rtm->rtm_type) {
152 	case RTM_ADD:
153 		if (gate == 0)
154 			senderr(EINVAL);
155 		error = rtrequest(RTM_ADD, dst, gate, netmask,
156 					rtm->rtm_flags, &saved_nrt);
157 		if (error == 0 && saved_nrt) {
158 			rt_setmetrics(rtm->rtm_inits,
159 				&rtm->rtm_rmx, &saved_nrt->rt_rmx);
160 			saved_nrt->rt_refcnt--;
161 			saved_nrt->rt_genmask = genmask;
162 		}
163 		break;
164 
165 	case RTM_DELETE:
166 		error = rtrequest(RTM_DELETE, dst, gate, netmask,
167 				rtm->rtm_flags, (struct rtentry **)0);
168 		break;
169 
170 	case RTM_GET:
171 	case RTM_CHANGE:
172 	case RTM_LOCK:
173 		rt = rtalloc1(dst, 0);
174 		if (rt == 0)
175 			senderr(ESRCH);
176 		switch(rtm->rtm_type) {
177 			 struct	sockaddr *outmask;
178 
179 		case RTM_GET:
180 			netmask = rt_mask(rt);
181 			len = sizeof(*rtm) + ROUNDUP(rt_key(rt)->sa_len);
182 			rtm->rtm_addrs = RTA_DST;
183 			if (rt->rt_gateway) {
184 				len += ROUNDUP(rt->rt_gateway->sa_len);
185 				rtm->rtm_addrs |= RTA_GATEWAY;
186 			}
187 			if (netmask) {
188 				len += netmask->sa_len;
189 				rtm->rtm_addrs |= RTA_NETMASK;
190 			}
191 			if (len > rtm->rtm_msglen) {
192 				struct rt_msghdr *new_rtm;
193 				R_Malloc(new_rtm, struct rt_msghdr *, len);
194 				if (new_rtm == 0)
195 					senderr(ENOBUFS);
196 				Bcopy(rtm, new_rtm, rtm->rtm_msglen);
197 				Free(rtm); rtm = new_rtm;
198 				gate = (struct sockaddr *)
199 				    (ROUNDUP(rt->rt_gateway->sa_len)
200 								+ (char *)dst);
201 				Bcopy(&rt->rt_gateway, gate,
202 						rt->rt_gateway->sa_len);
203 				rtm->rtm_flags = rt->rt_flags;
204 				if (netmask) {
205 				    outmask = (struct sockaddr *)
206 				       (ROUNDUP(netmask->sa_len)+(char *)gate);
207 				    Bcopy(netmask, outmask, netmask->sa_len);
208 				}
209 			}
210 			break;
211 
212 		case RTM_CHANGE:
213 			if (gate == 0 || netmask != 0)
214 				senderr(EINVAL);
215 			if (gate->sa_len > (len = rt->rt_gateway->sa_len))
216 				senderr(EDQUOT);
217 			if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
218 				rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, gate);
219 			/* new gateway could require new ifaddr, ifp;
220 			   flags may also be different; ifp may be specified
221 			   by ll sockaddr when protocol address is ambiguous */
222 			if (ifpaddr &&
223 			    (ifa = ifa_ifwithnet(ifpaddr)) &&
224 			    (ifp = ifa->ifa_ifp) &&
225 			    (ifa = ifaof_ifpforaddr(gate, ifp))) {
226 				     /* We got it */
227 			} else {
228 			    ifa = 0; ifp = 0;
229 			}
230 			Bcopy(gate, rt->rt_gateway, len);
231 			rt->rt_gateway->sa_len = len;
232 			rt_setmetrics(rtm->rtm_inits,
233 				&rtm->rtm_rmx, &rt->rt_rmx);
234 			if (ifa == 0)
235 			    ifa = ifa_ifwithroute(rt->rt_flags, rt_key(rt),
236 						gate);
237 			if (ifa) {
238 				if (rt->rt_ifa != ifa) {
239 				    rt->rt_ifa = ifa;
240 				    rt->rt_ifp = ifa->ifa_ifp;
241 				}
242 			}
243 			if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
244 			       rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, gate);
245 			if (genmask)
246 				rt->rt_genmask = genmask;
247 			/*
248 			 * Fall into
249 			 */
250 		case RTM_LOCK:
251 			rt->rt_rmx.rmx_locks |=
252 				(rtm->rtm_inits & rtm->rtm_rmx.rmx_locks);
253 			rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
254 			break;
255 		}
256 		goto cleanup;
257 
258 	default:
259 		senderr(EOPNOTSUPP);
260 	}
261 
262 flush:
263 	if (rtm) {
264 		if (error)
265 			rtm->rtm_errno = error;
266 		else
267 			rtm->rtm_flags |= RTF_DONE;
268 	}
269 cleanup:
270 	if (rt)
271 		rtfree(rt);
272     {
273 	register struct rawcb *rp = 0;
274 	/*
275 	 * Check to see if we don't want our own messages.
276 	 */
277 	if ((so->so_options & SO_USELOOPBACK) == 0) {
278 		if (route_cb.any_count <= 1) {
279 			if (rtm)
280 				Free(rtm);
281 			m_freem(m);
282 			return (error);
283 		}
284 		/* There is another listener, so construct message */
285 		rp = sotorawcb(so);
286 	}
287 	if (cp = (caddr_t)rtm) {
288 		m_copyback(m, 0, len, cp);
289 		Free(rtm);
290 	}
291 	if (rp)
292 		rp->rcb_proto.sp_family = 0; /* Avoid us */
293 	route_proto.sp_protocol = dst->sa_family;
294 	raw_input(m, &route_proto, &route_src, &route_dst);
295 	if (rp)
296 		rp->rcb_proto.sp_family = PF_ROUTE;
297     }
298 	return (error);
299 }
300 
301 rt_setmetrics(which, in, out)
302 	u_long which;
303 	register struct rt_metrics *in, *out;
304 {
305 #define metric(f, e) if (which & (f)) out->e = in->e;
306 	metric(RTV_RPIPE, rmx_recvpipe);
307 	metric(RTV_SPIPE, rmx_sendpipe);
308 	metric(RTV_SSTHRESH, rmx_ssthresh);
309 	metric(RTV_RTT, rmx_rtt);
310 	metric(RTV_RTTVAR, rmx_rttvar);
311 	metric(RTV_HOPCOUNT, rmx_hopcount);
312 	metric(RTV_MTU, rmx_mtu);
313 #undef metric
314 }
315 
316 /*
317  * Copy data from a buffer back into the indicated mbuf chain,
318  * starting "off" bytes from the beginning, extending the mbuf
319  * chain if necessary.
320  */
321 m_copyback(m0, off, len, cp)
322 	struct	mbuf *m0;
323 	register int off;
324 	register int len;
325 	caddr_t cp;
326 
327 {
328 	register int mlen;
329 	register struct mbuf *m = m0, *n;
330 	int totlen = 0;
331 
332 	if (m0 == 0)
333 		return;
334 	while (off >= (mlen = m->m_len)) {
335 		off -= mlen;
336 		totlen += mlen;
337 		if (m->m_next == 0) {
338 			n = m_getclr(M_DONTWAIT, m->m_type);
339 			if (n == 0)
340 				goto out;
341 			n->m_len = min(MLEN, len + off);
342 			m->m_next = n;
343 		}
344 		m = m->m_next;
345 	}
346 	while (len > 0) {
347 		mlen = min (m->m_len - off, len);
348 		bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
349 		cp += mlen;
350 		len -= mlen;
351 		mlen += off;
352 		off = 0;
353 		totlen += mlen;
354 		if (len == 0)
355 			break;
356 		if (m->m_next == 0) {
357 			n = m_get(M_DONTWAIT, m->m_type);
358 			if (n == 0)
359 				break;
360 			n->m_len = min(MLEN, len);
361 			m->m_next = n;
362 		}
363 		m = m->m_next;
364 	}
365 out:	if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
366 		m->m_pkthdr.len = totlen;
367 }
368 
369 /*
370  * The miss message and losing message are very similar.
371  */
372 
373 rt_missmsg(type, dst, gate, mask, src, flags, error)
374 register struct sockaddr *dst;
375 struct sockaddr *gate, *mask, *src;
376 {
377 	register struct rt_msghdr *rtm;
378 	register struct mbuf *m;
379 	int dlen = ROUNDUP(dst->sa_len);
380 	int len = dlen + sizeof(*rtm);
381 
382 	if (route_cb.any_count == 0)
383 		return;
384 	m = m_gethdr(M_DONTWAIT, MT_DATA);
385 	if (m == 0)
386 		return;
387 	m->m_pkthdr.len = m->m_len = min(len, MHLEN);
388 	m->m_pkthdr.rcvif = 0;
389 	rtm = mtod(m, struct rt_msghdr *);
390 	bzero((caddr_t)rtm, sizeof(*rtm)); /*XXX assumes sizeof(*rtm) < MHLEN*/
391 	rtm->rtm_flags = RTF_DONE | flags;
392 	rtm->rtm_msglen = len;
393 	rtm->rtm_version = RTM_VERSION;
394 	rtm->rtm_type = type;
395 	rtm->rtm_addrs = RTA_DST;
396 	if (type == RTM_OLDADD || type == RTM_OLDDEL) {
397 		rtm->rtm_pid = u.u_procp->p_pid;
398 	}
399 	m_copyback(m, sizeof (*rtm), dlen, (caddr_t)dst);
400 	if (gate) {
401 		dlen = ROUNDUP(gate->sa_len);
402 		m_copyback(m, len ,  dlen, (caddr_t)gate);
403 		len += dlen;
404 		rtm->rtm_addrs |= RTA_GATEWAY;
405 	}
406 	if (mask) {
407 		if (mask->sa_len)
408 			dlen = ROUNDUP(mask->sa_len);
409 		else
410 			dlen = sizeof(long);
411 		m_copyback(m, len ,  dlen, (caddr_t)mask);
412 		len += dlen;
413 		rtm->rtm_addrs |= RTA_NETMASK;
414 	}
415 	if (src) {
416 		dlen = ROUNDUP(src->sa_len);
417 		m_copyback(m, len ,  dlen, (caddr_t)src);
418 		len += dlen;
419 		rtm->rtm_addrs |= RTA_AUTHOR;
420 	}
421 	if (m->m_pkthdr.len != len) {
422 		m_freem(m);
423 		return;
424 	}
425 	rtm->rtm_errno = error;
426 	rtm->rtm_msglen = len;
427 	route_proto.sp_protocol = dst->sa_family;
428 	raw_input(m, &route_proto, &route_src, &route_dst);
429 }
430 
431 #include "kinfo.h"
432 struct walkarg {
433 	int	w_op, w_arg;
434 	int	w_given, w_needed;
435 	caddr_t	w_where;
436 	struct	{
437 		struct rt_msghdr m_rtm;
438 		char	m_sabuf[128];
439 	} w_m;
440 #define w_rtm w_m.m_rtm
441 };
442 /*
443  * This is used in dumping the kernel table via getkinfo().
444  */
445 rt_dumpentry(rn, w)
446 	struct radix_node *rn;
447 	register struct walkarg *w;
448 {
449 	register struct sockaddr *sa;
450 	int n, error;
451 
452     for (; rn; rn = rn->rn_dupedkey) {
453 	int count = 0, size = sizeof(w->w_rtm);
454 	register struct rtentry *rt = (struct rtentry *)rn;
455 
456 	if (rn->rn_flags & RNF_ROOT)
457 		continue;
458 	if (w->w_op == KINFO_RT_FLAGS && !(rt->rt_flags & w->w_arg))
459 		continue;
460 #define next(a, l) {size += (l); w->w_rtm.rtm_addrs |= (a); }
461 	w->w_rtm.rtm_addrs = 0;
462 	if (sa = rt_key(rt))
463 		next(RTA_DST, ROUNDUP(sa->sa_len));
464 	if (sa = rt->rt_gateway)
465 		next(RTA_GATEWAY, ROUNDUP(sa->sa_len));
466 	if (sa = rt_mask(rt))
467 		next(RTA_NETMASK,
468 			sa->sa_len ? ROUNDUP(sa->sa_len) : sizeof(long));
469 	if (sa = rt->rt_genmask)
470 		next(RTA_GENMASK, ROUNDUP(sa->sa_len));
471 	w->w_needed += size;
472 	if (w->w_where == NULL || w->w_needed > 0)
473 		continue;
474 	w->w_rtm.rtm_msglen = size;
475 	w->w_rtm.rtm_flags = rt->rt_flags;
476 	w->w_rtm.rtm_use = rt->rt_use;
477 	w->w_rtm.rtm_rmx = rt->rt_rmx;
478 	w->w_rtm.rtm_index = rt->rt_ifp->if_index;
479 #undef next
480 #define next(l) {n = (l); Bcopy(sa, cp, n); cp += n;}
481 	if (size <= sizeof(w->w_m)) {
482 		register caddr_t cp = (caddr_t)(w->w_m.m_sabuf);
483 		if (sa = rt_key(rt))
484 			next(ROUNDUP(sa->sa_len));
485 		if (sa = rt->rt_gateway)
486 			next(ROUNDUP(sa->sa_len));
487 		if (sa = rt_mask(rt))
488 			next(sa->sa_len ? ROUNDUP(sa->sa_len) : sizeof(long));
489 		if (sa = rt->rt_genmask)
490 			next(ROUNDUP(sa->sa_len));
491 #undef next
492 #define next(s, l) {n = (l); \
493     if (error = copyout((caddr_t)(s), w->w_where, n)) return (error); \
494     w->w_where += n;}
495 
496 		next(&w->w_m, size); /* Copy rtmsg and sockaddrs back */
497 		continue;
498 	}
499 	next(&w->w_rtm, sizeof(w->w_rtm));
500 	if (sa = rt_key(rt))
501 		next(sa, ROUNDUP(sa->sa_len));
502 	if (sa = rt->rt_gateway)
503 		next(sa, ROUNDUP(sa->sa_len));
504 	if (sa = rt_mask(rt))
505 		next(sa, sa->sa_len ? ROUNDUP(sa->sa_len) : sizeof(long));
506 	if (sa = rt->rt_genmask)
507 		next(sa, ROUNDUP(sa->sa_len));
508     }
509 	return (0);
510 #undef next
511 }
512 
513 kinfo_rtable(op, where, given, arg, needed)
514 	int	op, arg;
515 	caddr_t	where;
516 	int	*given, *needed;
517 {
518 	register struct radix_node_head *rnh;
519 	int	s, error = 0;
520 	u_char  af = ki_af(op);
521 	struct	walkarg w;
522 
523 	op &= 0xffff;
524 	if (op != KINFO_RT_DUMP && op != KINFO_RT_FLAGS)
525 		return (EINVAL);
526 
527 	Bzero(&w, sizeof(w));
528 	if ((w.w_where = where) && given)
529 		w.w_given = *given;
530 	w.w_needed = 0 - w.w_given;
531 	w.w_arg = arg;
532 	w.w_op = op;
533 	w.w_rtm.rtm_version = RTM_VERSION;
534 	w.w_rtm.rtm_type = RTM_GET;
535 
536 	s = splnet();
537 	for (rnh = radix_node_head; rnh; rnh = rnh->rnh_next) {
538 		if (rnh->rnh_af == 0)
539 			continue;
540 		if (af && af != rnh->rnh_af)
541 			continue;
542 		error = rt_walk(rnh->rnh_treetop, rt_dumpentry, &w);
543 		if (error)
544 			break;
545 	}
546 	w.w_needed += w.w_given;
547 	if (where && given)
548 		*given = w.w_where - where;
549 	else
550 		w.w_needed = (11 * w.w_needed) / 10;
551 	*needed = w.w_needed;
552 	splx(s);
553 	return (error);
554 }
555 
556 rt_walk(rn, f, w)
557 	register struct radix_node *rn;
558 	register int (*f)();
559 	struct walkarg *w;
560 {
561 	int error;
562 	for (;;) {
563 		while (rn->rn_b >= 0)
564 			rn = rn->rn_l;	/* First time through node, go left */
565 		if (error = (*f)(rn, w))
566 			return (error);	/* Process Leaf */
567 		while (rn->rn_p->rn_r == rn) {	/* if coming back from right */
568 			rn = rn->rn_p;		/* go back up */
569 			if (rn->rn_flags & RNF_ROOT)
570 				return 0;
571 		}
572 		rn = rn->rn_p->rn_r;		/* otherwise, go right*/
573 	}
574 }
575 
576 /*
577  * Definitions of protocols supported in the ROUTE domain.
578  */
579 
580 int	raw_init(),raw_usrreq(),raw_input(),raw_ctlinput();
581 extern	struct domain routedomain;		/* or at least forward */
582 
583 struct protosw routesw[] = {
584 { SOCK_RAW,	&routedomain,	0,		PR_ATOMIC|PR_ADDR,
585   raw_input,	route_output,	raw_ctlinput,	0,
586   route_usrreq,
587   raw_init,	0,		0,		0,
588 }
589 };
590 
591 int	unp_externalize(), unp_dispose();
592 
593 struct domain routedomain =
594     { PF_ROUTE, "route", 0, 0, 0,
595       routesw, &routesw[sizeof(routesw)/sizeof(routesw[0])] };
596