Commit | Line | Data |
---|---|---|
cf040064 KS |
1 | /* |
2 | * Copyright (c) 1989 Stephen Deering | |
ad787160 C |
3 | * Copyright (c) 1992, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
cf040064 KS |
5 | * |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Stephen Deering of Stanford University. | |
8 | * | |
ad787160 C |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
17 | * 3. All advertising materials mentioning features or use of this software | |
18 | * must display the following acknowledgement: | |
19 | * This product includes software developed by the University of | |
20 | * California, Berkeley and its contributors. | |
21 | * 4. Neither the name of the University nor the names of its contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
cf040064 | 24 | * |
ad787160 C |
25 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
26 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
34 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
35 | * SUCH DAMAGE. | |
36 | * | |
ed554bc5 | 37 | * @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93 |
cf040064 KS |
38 | */ |
39 | ||
40 | /* | |
41 | * Procedures for the kernel part of DVMRP, | |
42 | * a Distance-Vector Multicast Routing Protocol. | |
43 | * (See RFC-1075.) | |
44 | * | |
45 | * Written by David Waitzman, BBN Labs, August 1988. | |
46 | * Modified by Steve Deering, Stanford, February 1989. | |
47 | * | |
48 | * MROUTING 1.1 | |
49 | */ | |
50 | ||
cf040064 KS |
51 | #ifndef MROUTING |
52 | int ip_mrtproto; /* for netstat only */ | |
53 | #else | |
54 | ||
5548a02f KB |
55 | #include <sys/param.h> |
56 | #include <sys/errno.h> | |
57 | #include <sys/ioctl.h> | |
58 | #include <sys/malloc.h> | |
59 | #include <sys/mbuf.h> | |
60 | #include <sys/protosw.h> | |
61 | #include <sys/socket.h> | |
62 | #include <sys/socketvar.h> | |
63 | #include <sys/time.h> | |
cf040064 | 64 | |
5548a02f KB |
65 | #include <net/if.h> |
66 | #include <net/route.h> | |
67 | #include <net/raw_cb.h> | |
cf040064 | 68 | |
5548a02f KB |
69 | #include <netinet/in.h> |
70 | #include <netinet/in_systm.h> | |
71 | #include <netinet/ip.h> | |
72 | #include <netinet/in_pcb.h> | |
73 | #include <netinet/in_var.h> | |
74 | #include <netinet/ip_var.h> | |
cf040064 | 75 | |
5548a02f KB |
76 | #include <netinet/igmp.h> |
77 | #include <netinet/igmp_var.h> | |
78 | #include <netinet/ip_mroute.h> | |
cf040064 KS |
79 | |
80 | /* Static forwards */ | |
81 | static int ip_mrouter_init __P((struct socket *)); | |
82 | static int add_vif __P((struct vifctl *)); | |
83 | static int del_vif __P((vifi_t *vifip)); | |
84 | static int add_lgrp __P((struct lgrplctl *)); | |
85 | static int del_lgrp __P((struct lgrplctl *)); | |
86 | static int grplst_member __P((struct vif *, struct in_addr)); | |
87 | static u_long nethash __P((struct in_addr in)); | |
88 | static int add_mrt __P((struct mrtctl *)); | |
89 | static int del_mrt __P((struct in_addr *)); | |
90 | static struct mrt *mrtfind __P((struct in_addr)); | |
91 | static void phyint_send __P((struct mbuf *, struct vif *)); | |
92 | static void tunnel_send __P((struct mbuf *, struct vif *)); | |
93 | ||
94 | #define INSIZ sizeof(struct in_addr) | |
95 | #define same(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), INSIZ) == 0) | |
96 | #define satosin(sa) ((struct sockaddr_in *)(sa)) | |
97 | ||
98 | /* | |
99 | * Globals. All but ip_mrouter and ip_mrtproto could be static, | |
100 | * except for netstat or debugging purposes. | |
101 | */ | |
102 | struct socket *ip_mrouter = NULL; | |
103 | int ip_mrtproto = IGMP_DVMRP; /* for netstat only */ | |
104 | ||
105 | struct mrt *mrttable[MRTHASHSIZ]; | |
106 | struct vif viftable[MAXVIFS]; | |
107 | struct mrtstat mrtstat; | |
108 | ||
109 | /* | |
110 | * Private variables. | |
111 | */ | |
112 | static vifi_t numvifs = 0; | |
113 | static struct mrt *cached_mrt = NULL; | |
114 | static u_long cached_origin; | |
115 | static u_long cached_originmask; | |
116 | ||
117 | /* | |
118 | * Handle DVMRP setsockopt commands to modify the multicast routing tables. | |
119 | */ | |
120 | int | |
121 | ip_mrouter_cmd(cmd, so, m) | |
122 | register int cmd; | |
123 | register struct socket *so; | |
124 | register struct mbuf *m; | |
125 | { | |
126 | register int error = 0; | |
127 | ||
128 | if (cmd != DVMRP_INIT && so != ip_mrouter) | |
129 | error = EACCES; | |
130 | else switch (cmd) { | |
131 | ||
132 | case DVMRP_INIT: | |
133 | error = ip_mrouter_init(so); | |
134 | break; | |
135 | ||
136 | case DVMRP_DONE: | |
137 | error = ip_mrouter_done(); | |
138 | break; | |
139 | ||
140 | case DVMRP_ADD_VIF: | |
141 | if (m == NULL || m->m_len < sizeof(struct vifctl)) | |
142 | error = EINVAL; | |
143 | else | |
144 | error = add_vif(mtod(m, struct vifctl *)); | |
145 | break; | |
146 | ||
147 | case DVMRP_DEL_VIF: | |
148 | if (m == NULL || m->m_len < sizeof(short)) | |
149 | error = EINVAL; | |
150 | else | |
151 | error = del_vif(mtod(m, vifi_t *)); | |
152 | break; | |
153 | ||
154 | case DVMRP_ADD_LGRP: | |
155 | if (m == NULL || m->m_len < sizeof(struct lgrplctl)) | |
156 | error = EINVAL; | |
157 | else | |
158 | error = add_lgrp(mtod(m, struct lgrplctl *)); | |
159 | break; | |
160 | ||
161 | case DVMRP_DEL_LGRP: | |
162 | if (m == NULL || m->m_len < sizeof(struct lgrplctl)) | |
163 | error = EINVAL; | |
164 | else | |
165 | error = del_lgrp(mtod(m, struct lgrplctl *)); | |
166 | break; | |
167 | ||
168 | case DVMRP_ADD_MRT: | |
169 | if (m == NULL || m->m_len < sizeof(struct mrtctl)) | |
170 | error = EINVAL; | |
171 | else | |
172 | error = add_mrt(mtod(m, struct mrtctl *)); | |
173 | break; | |
174 | ||
175 | case DVMRP_DEL_MRT: | |
176 | if (m == NULL || m->m_len < sizeof(struct in_addr)) | |
177 | error = EINVAL; | |
178 | else | |
179 | error = del_mrt(mtod(m, struct in_addr *)); | |
180 | break; | |
181 | ||
182 | default: | |
183 | error = EOPNOTSUPP; | |
184 | break; | |
185 | } | |
186 | return (error); | |
187 | } | |
188 | ||
189 | /* | |
190 | * Enable multicast routing | |
191 | */ | |
192 | static int | |
193 | ip_mrouter_init(so) | |
194 | register struct socket *so; | |
195 | { | |
196 | if (so->so_type != SOCK_RAW || | |
197 | so->so_proto->pr_protocol != IPPROTO_IGMP) | |
198 | return (EOPNOTSUPP); | |
199 | ||
200 | if (ip_mrouter != NULL) | |
201 | return (EADDRINUSE); | |
202 | ||
203 | ip_mrouter = so; | |
204 | ||
205 | return (0); | |
206 | } | |
207 | ||
208 | /* | |
209 | * Disable multicast routing | |
210 | */ | |
211 | int | |
212 | ip_mrouter_done() | |
213 | { | |
214 | register vifi_t vifi; | |
215 | register int i; | |
216 | register struct ifnet *ifp; | |
217 | register int s; | |
218 | struct ifreq ifr; | |
219 | ||
220 | s = splnet(); | |
221 | ||
222 | /* | |
223 | * For each phyint in use, free its local group list and | |
224 | * disable promiscuous reception of all IP multicasts. | |
225 | */ | |
226 | for (vifi = 0; vifi < numvifs; vifi++) { | |
227 | if (viftable[vifi].v_lcl_addr.s_addr != 0 && | |
228 | !(viftable[vifi].v_flags & VIFF_TUNNEL)) { | |
229 | if (viftable[vifi].v_lcl_grps) | |
230 | free(viftable[vifi].v_lcl_grps, M_MRTABLE); | |
231 | satosin(&ifr.ifr_addr)->sin_family = AF_INET; | |
232 | satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY; | |
233 | ifp = viftable[vifi].v_ifp; | |
234 | (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); | |
235 | } | |
236 | } | |
237 | bzero((caddr_t)viftable, sizeof(viftable)); | |
238 | numvifs = 0; | |
239 | ||
240 | /* | |
241 | * Free any multicast route entries. | |
242 | */ | |
243 | for (i = 0; i < MRTHASHSIZ; i++) | |
244 | if (mrttable[i]) | |
245 | free(mrttable[i], M_MRTABLE); | |
246 | bzero((caddr_t)mrttable, sizeof(mrttable)); | |
247 | cached_mrt = NULL; | |
248 | ||
249 | ip_mrouter = NULL; | |
250 | ||
251 | splx(s); | |
252 | return (0); | |
253 | } | |
254 | ||
255 | /* | |
256 | * Add a vif to the vif table | |
257 | */ | |
258 | static int | |
259 | add_vif(vifcp) | |
260 | register struct vifctl *vifcp; | |
261 | { | |
262 | register struct vif *vifp = viftable + vifcp->vifc_vifi; | |
263 | register struct ifaddr *ifa; | |
264 | register struct ifnet *ifp; | |
265 | struct ifreq ifr; | |
266 | register int error, s; | |
267 | static struct sockaddr_in sin = { sizeof(sin), AF_INET }; | |
268 | ||
269 | if (vifcp->vifc_vifi >= MAXVIFS) | |
270 | return (EINVAL); | |
271 | if (vifp->v_lcl_addr.s_addr != 0) | |
272 | return (EADDRINUSE); | |
273 | ||
274 | /* Find the interface with an address in AF_INET family */ | |
275 | sin.sin_addr = vifcp->vifc_lcl_addr; | |
276 | ifa = ifa_ifwithaddr((struct sockaddr *)&sin); | |
277 | if (ifa == 0) | |
278 | return (EADDRNOTAVAIL); | |
279 | ||
280 | s = splnet(); | |
281 | ||
282 | if (vifcp->vifc_flags & VIFF_TUNNEL) | |
283 | vifp->v_rmt_addr = vifcp->vifc_rmt_addr; | |
284 | else { | |
285 | /* Make sure the interface supports multicast */ | |
286 | ifp = ifa->ifa_ifp; | |
287 | if ((ifp->if_flags & IFF_MULTICAST) == 0) { | |
288 | splx(s); | |
289 | return (EOPNOTSUPP); | |
290 | } | |
291 | /* | |
292 | * Enable promiscuous reception of all IP multicasts | |
293 | * from the interface. | |
294 | */ | |
295 | satosin(&ifr.ifr_addr)->sin_family = AF_INET; | |
296 | satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY; | |
297 | error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr); | |
298 | if (error) { | |
299 | splx(s); | |
300 | return (error); | |
301 | } | |
302 | } | |
303 | ||
304 | vifp->v_flags = vifcp->vifc_flags; | |
305 | vifp->v_threshold = vifcp->vifc_threshold; | |
306 | vifp->v_lcl_addr = vifcp->vifc_lcl_addr; | |
307 | vifp->v_ifp = ifa->ifa_ifp; | |
308 | ||
309 | /* Adjust numvifs up if the vifi is higher than numvifs */ | |
310 | if (numvifs <= vifcp->vifc_vifi) | |
311 | numvifs = vifcp->vifc_vifi + 1; | |
312 | ||
313 | splx(s); | |
314 | return (0); | |
315 | } | |
316 | ||
317 | /* | |
318 | * Delete a vif from the vif table | |
319 | */ | |
320 | static int | |
321 | del_vif(vifip) | |
322 | register vifi_t *vifip; | |
323 | { | |
324 | register struct vif *vifp = viftable + *vifip; | |
cf040064 | 325 | register struct ifnet *ifp; |
061fa49c | 326 | register int i, s; |
cf040064 KS |
327 | struct ifreq ifr; |
328 | ||
329 | if (*vifip >= numvifs) | |
330 | return (EINVAL); | |
331 | if (vifp->v_lcl_addr.s_addr == 0) | |
332 | return (EADDRNOTAVAIL); | |
333 | ||
334 | s = splnet(); | |
335 | ||
336 | if (!(vifp->v_flags & VIFF_TUNNEL)) { | |
337 | if (vifp->v_lcl_grps) | |
338 | free(vifp->v_lcl_grps, M_MRTABLE); | |
339 | satosin(&ifr.ifr_addr)->sin_family = AF_INET; | |
340 | satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY; | |
341 | ifp = vifp->v_ifp; | |
342 | (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); | |
343 | } | |
344 | ||
345 | bzero((caddr_t)vifp, sizeof (*vifp)); | |
346 | ||
347 | /* Adjust numvifs down */ | |
061fa49c CT |
348 | for (i = numvifs - 1; i >= 0; i--) |
349 | if (viftable[i].v_lcl_addr.s_addr != 0) | |
cf040064 | 350 | break; |
061fa49c | 351 | numvifs = i + 1; |
cf040064 KS |
352 | |
353 | splx(s); | |
354 | return (0); | |
355 | } | |
356 | ||
357 | /* | |
358 | * Add the multicast group in the lgrpctl to the list of local multicast | |
359 | * group memberships associated with the vif indexed by gcp->lgc_vifi. | |
360 | */ | |
361 | static int | |
362 | add_lgrp(gcp) | |
363 | register struct lgrplctl *gcp; | |
364 | { | |
365 | register struct vif *vifp; | |
366 | register int s; | |
367 | ||
368 | if (gcp->lgc_vifi >= numvifs) | |
369 | return (EINVAL); | |
370 | ||
371 | vifp = viftable + gcp->lgc_vifi; | |
372 | if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL)) | |
373 | return (EADDRNOTAVAIL); | |
374 | ||
375 | /* If not enough space in existing list, allocate a larger one */ | |
376 | s = splnet(); | |
377 | if (vifp->v_lcl_grps_n + 1 >= vifp->v_lcl_grps_max) { | |
378 | register int num; | |
379 | register struct in_addr *ip; | |
380 | ||
381 | num = vifp->v_lcl_grps_max; | |
382 | if (num <= 0) | |
383 | num = 32; /* initial number */ | |
384 | else | |
385 | num += num; /* double last number */ | |
386 | ip = (struct in_addr *)malloc(num * sizeof(*ip), | |
387 | M_MRTABLE, M_NOWAIT); | |
388 | if (ip == NULL) { | |
389 | splx(s); | |
390 | return (ENOBUFS); | |
391 | } | |
392 | ||
393 | bzero((caddr_t)ip, num * sizeof(*ip)); /* XXX paranoid */ | |
394 | bcopy((caddr_t)vifp->v_lcl_grps, (caddr_t)ip, | |
395 | vifp->v_lcl_grps_n * sizeof(*ip)); | |
396 | ||
397 | vifp->v_lcl_grps_max = num; | |
398 | if (vifp->v_lcl_grps) | |
399 | free(vifp->v_lcl_grps, M_MRTABLE); | |
400 | vifp->v_lcl_grps = ip; | |
401 | ||
402 | splx(s); | |
403 | } | |
404 | ||
405 | vifp->v_lcl_grps[vifp->v_lcl_grps_n++] = gcp->lgc_gaddr; | |
406 | ||
407 | if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group) | |
408 | vifp->v_cached_result = 1; | |
409 | ||
410 | splx(s); | |
411 | return (0); | |
412 | } | |
413 | ||
414 | /* | |
415 | * Delete the the local multicast group associated with the vif | |
416 | * indexed by gcp->lgc_vifi. | |
417 | */ | |
418 | ||
419 | static int | |
420 | del_lgrp(gcp) | |
421 | register struct lgrplctl *gcp; | |
422 | { | |
423 | register struct vif *vifp; | |
424 | register int i, error, s; | |
425 | ||
426 | if (gcp->lgc_vifi >= numvifs) | |
427 | return (EINVAL); | |
428 | vifp = viftable + gcp->lgc_vifi; | |
429 | if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL)) | |
430 | return (EADDRNOTAVAIL); | |
431 | ||
432 | s = splnet(); | |
433 | ||
434 | if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group) | |
435 | vifp->v_cached_result = 0; | |
436 | ||
437 | error = EADDRNOTAVAIL; | |
438 | for (i = 0; i < vifp->v_lcl_grps_n; ++i) | |
439 | if (same(&gcp->lgc_gaddr, &vifp->v_lcl_grps[i])) { | |
440 | error = 0; | |
441 | vifp->v_lcl_grps_n--; | |
442 | bcopy((caddr_t)&vifp->v_lcl_grps[i + 1], | |
443 | (caddr_t)&vifp->v_lcl_grps[i], | |
444 | (vifp->v_lcl_grps_n - i) * sizeof(struct in_addr)); | |
445 | error = 0; | |
446 | break; | |
447 | } | |
448 | ||
449 | splx(s); | |
450 | return (error); | |
451 | } | |
452 | ||
453 | /* | |
454 | * Return 1 if gaddr is a member of the local group list for vifp. | |
455 | */ | |
456 | static int | |
457 | grplst_member(vifp, gaddr) | |
458 | register struct vif *vifp; | |
459 | struct in_addr gaddr; | |
460 | { | |
461 | register int i, s; | |
462 | register u_long addr; | |
463 | ||
464 | mrtstat.mrts_grp_lookups++; | |
465 | ||
466 | addr = gaddr.s_addr; | |
467 | if (addr == vifp->v_cached_group) | |
468 | return (vifp->v_cached_result); | |
469 | ||
470 | mrtstat.mrts_grp_misses++; | |
471 | ||
472 | for (i = 0; i < vifp->v_lcl_grps_n; ++i) | |
473 | if (addr == vifp->v_lcl_grps[i].s_addr) { | |
474 | s = splnet(); | |
475 | vifp->v_cached_group = addr; | |
476 | vifp->v_cached_result = 1; | |
477 | splx(s); | |
478 | return (1); | |
479 | } | |
480 | s = splnet(); | |
481 | vifp->v_cached_group = addr; | |
482 | vifp->v_cached_result = 0; | |
483 | splx(s); | |
484 | return (0); | |
485 | } | |
486 | ||
487 | /* | |
488 | * A simple hash function: returns MRTHASHMOD of the low-order octet of | |
489 | * the argument's network or subnet number. | |
490 | */ | |
491 | static u_long | |
492 | nethash(in) | |
493 | struct in_addr in; | |
494 | { | |
495 | register u_long n; | |
496 | ||
497 | n = in_netof(in); | |
498 | while ((n & 0xff) == 0) | |
499 | n >>= 8; | |
500 | return (MRTHASHMOD(n)); | |
501 | } | |
502 | ||
503 | /* | |
504 | * Add an mrt entry | |
505 | */ | |
506 | static int | |
507 | add_mrt(mrtcp) | |
508 | register struct mrtctl *mrtcp; | |
509 | { | |
510 | struct mrt *rt; | |
511 | u_long hash; | |
512 | int s; | |
513 | ||
514 | if (rt = mrtfind(mrtcp->mrtc_origin)) { | |
515 | /* Just update the route */ | |
516 | s = splnet(); | |
517 | rt->mrt_parent = mrtcp->mrtc_parent; | |
518 | VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children); | |
519 | VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves); | |
520 | splx(s); | |
521 | return (0); | |
522 | } | |
523 | ||
524 | s = splnet(); | |
525 | ||
526 | rt = (struct mrt *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT); | |
527 | if (rt == NULL) { | |
528 | splx(s); | |
529 | return (ENOBUFS); | |
530 | } | |
531 | ||
532 | /* | |
533 | * insert new entry at head of hash chain | |
534 | */ | |
535 | rt->mrt_origin = mrtcp->mrtc_origin; | |
536 | rt->mrt_originmask = mrtcp->mrtc_originmask; | |
537 | rt->mrt_parent = mrtcp->mrtc_parent; | |
538 | VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children); | |
539 | VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves); | |
540 | /* link into table */ | |
541 | hash = nethash(mrtcp->mrtc_origin); | |
542 | rt->mrt_next = mrttable[hash]; | |
543 | mrttable[hash] = rt; | |
544 | ||
545 | splx(s); | |
546 | return (0); | |
547 | } | |
548 | ||
549 | /* | |
550 | * Delete an mrt entry | |
551 | */ | |
552 | static int | |
553 | del_mrt(origin) | |
554 | register struct in_addr *origin; | |
555 | { | |
556 | register struct mrt *rt, *prev_rt; | |
557 | register u_long hash = nethash(*origin); | |
558 | register int s; | |
559 | ||
560 | for (prev_rt = rt = mrttable[hash]; rt; prev_rt = rt, rt = rt->mrt_next) | |
561 | if (origin->s_addr == rt->mrt_origin.s_addr) | |
562 | break; | |
563 | if (!rt) | |
564 | return (ESRCH); | |
565 | ||
566 | s = splnet(); | |
567 | ||
568 | if (rt == cached_mrt) | |
569 | cached_mrt = NULL; | |
570 | ||
571 | if (prev_rt == rt) | |
572 | mrttable[hash] = rt->mrt_next; | |
573 | else | |
574 | prev_rt->mrt_next = rt->mrt_next; | |
575 | free(rt, M_MRTABLE); | |
576 | ||
577 | splx(s); | |
578 | return (0); | |
579 | } | |
580 | ||
581 | /* | |
582 | * Find a route for a given origin IP address. | |
583 | */ | |
584 | static struct mrt * | |
585 | mrtfind(origin) | |
586 | struct in_addr origin; | |
587 | { | |
588 | register struct mrt *rt; | |
589 | register u_int hash; | |
590 | register int s; | |
591 | ||
592 | mrtstat.mrts_mrt_lookups++; | |
593 | ||
594 | if (cached_mrt != NULL && | |
595 | (origin.s_addr & cached_originmask) == cached_origin) | |
596 | return (cached_mrt); | |
597 | ||
598 | mrtstat.mrts_mrt_misses++; | |
599 | ||
600 | hash = nethash(origin); | |
601 | for (rt = mrttable[hash]; rt; rt = rt->mrt_next) | |
602 | if ((origin.s_addr & rt->mrt_originmask.s_addr) == | |
603 | rt->mrt_origin.s_addr) { | |
604 | s = splnet(); | |
605 | cached_mrt = rt; | |
606 | cached_origin = rt->mrt_origin.s_addr; | |
607 | cached_originmask = rt->mrt_originmask.s_addr; | |
608 | splx(s); | |
609 | return (rt); | |
610 | } | |
611 | return (NULL); | |
612 | } | |
613 | ||
614 | /* | |
615 | * IP multicast forwarding function. This function assumes that the packet | |
616 | * pointed to by "ip" has arrived on (or is about to be sent to) the interface | |
617 | * pointed to by "ifp", and the packet is to be relayed to other networks | |
618 | * that have members of the packet's destination IP multicast group. | |
619 | * | |
620 | * The packet is returned unscathed to the caller, unless it is tunneled | |
621 | * or erroneous, in which case a non-zero return value tells the caller to | |
622 | * discard it. | |
623 | */ | |
624 | ||
625 | #define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */ | |
626 | #define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */ | |
627 | ||
628 | int | |
629 | ip_mforward(m, ifp) | |
630 | register struct mbuf *m; | |
631 | register struct ifnet *ifp; | |
632 | { | |
633 | register struct ip *ip = mtod(m, struct ip *); | |
634 | register struct mrt *rt; | |
635 | register struct vif *vifp; | |
636 | register int vifi; | |
637 | register u_char *ipoptions; | |
638 | u_long tunnel_src; | |
639 | ||
640 | if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 || | |
641 | (ipoptions = (u_char *)(ip + 1))[1] != IPOPT_LSRR ) { | |
642 | /* | |
643 | * Packet arrived via a physical interface. | |
644 | */ | |
645 | tunnel_src = 0; | |
646 | } else { | |
647 | /* | |
648 | * Packet arrived through a tunnel. | |
649 | * | |
650 | * A tunneled packet has a single NOP option and a | |
651 | * two-element loose-source-and-record-route (LSRR) | |
652 | * option immediately following the fixed-size part of | |
653 | * the IP header. At this point in processing, the IP | |
654 | * header should contain the following IP addresses: | |
655 | * | |
656 | * original source - in the source address field | |
657 | * destination group - in the destination address field | |
658 | * remote tunnel end-point - in the first element of LSRR | |
659 | * one of this host's addrs - in the second element of LSRR | |
660 | * | |
661 | * NOTE: RFC-1075 would have the original source and | |
662 | * remote tunnel end-point addresses swapped. However, | |
663 | * that could cause delivery of ICMP error messages to | |
664 | * innocent applications on intermediate routing | |
665 | * hosts! Therefore, we hereby change the spec. | |
666 | */ | |
667 | ||
668 | /* | |
669 | * Verify that the tunnel options are well-formed. | |
670 | */ | |
671 | if (ipoptions[0] != IPOPT_NOP || | |
672 | ipoptions[2] != 11 || /* LSRR option length */ | |
673 | ipoptions[3] != 12 || /* LSRR address pointer */ | |
674 | (tunnel_src = *(u_long *)(&ipoptions[4])) == 0) { | |
675 | mrtstat.mrts_bad_tunnel++; | |
676 | return (1); | |
677 | } | |
678 | ||
679 | /* | |
680 | * Delete the tunnel options from the packet. | |
681 | */ | |
682 | ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions, | |
683 | (unsigned)(m->m_len - (IP_HDR_LEN + TUNNEL_LEN))); | |
684 | m->m_len -= TUNNEL_LEN; | |
685 | ip->ip_len -= TUNNEL_LEN; | |
686 | ip->ip_hl -= TUNNEL_LEN >> 2; | |
687 | } | |
688 | ||
689 | /* | |
690 | * Don't forward a packet with time-to-live of zero or one, | |
691 | * or a packet destined to a local-only group. | |
692 | */ | |
693 | if (ip->ip_ttl <= 1 || | |
694 | ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP) | |
695 | return ((int)tunnel_src); | |
696 | ||
697 | /* | |
698 | * Don't forward if we don't have a route for the packet's origin. | |
699 | */ | |
700 | if (!(rt = mrtfind(ip->ip_src))) { | |
701 | mrtstat.mrts_no_route++; | |
702 | return ((int)tunnel_src); | |
703 | } | |
704 | ||
705 | /* | |
706 | * Don't forward if it didn't arrive from the parent vif for its origin. | |
707 | */ | |
708 | vifi = rt->mrt_parent; | |
709 | if (tunnel_src == 0 ) { | |
710 | if ((viftable[vifi].v_flags & VIFF_TUNNEL) || | |
711 | viftable[vifi].v_ifp != ifp ) | |
712 | return ((int)tunnel_src); | |
713 | } else { | |
714 | if (!(viftable[vifi].v_flags & VIFF_TUNNEL) || | |
715 | viftable[vifi].v_rmt_addr.s_addr != tunnel_src ) | |
716 | return ((int)tunnel_src); | |
717 | } | |
718 | ||
719 | /* | |
720 | * For each vif, decide if a copy of the packet should be forwarded. | |
721 | * Forward if: | |
722 | * - the ttl exceeds the vif's threshold AND | |
723 | * - the vif is a child in the origin's route AND | |
724 | * - ( the vif is not a leaf in the origin's route OR | |
725 | * the destination group has members on the vif ) | |
726 | * | |
727 | * (This might be speeded up with some sort of cache -- someday.) | |
728 | */ | |
729 | for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) { | |
730 | if (ip->ip_ttl > vifp->v_threshold && | |
731 | VIFM_ISSET(vifi, rt->mrt_children) && | |
732 | (!VIFM_ISSET(vifi, rt->mrt_leaves) || | |
733 | grplst_member(vifp, ip->ip_dst))) { | |
734 | if (vifp->v_flags & VIFF_TUNNEL) | |
735 | tunnel_send(m, vifp); | |
736 | else | |
737 | phyint_send(m, vifp); | |
738 | } | |
739 | } | |
740 | ||
741 | return ((int)tunnel_src); | |
742 | } | |
743 | ||
744 | static void | |
745 | phyint_send(m, vifp) | |
746 | register struct mbuf *m; | |
747 | register struct vif *vifp; | |
748 | { | |
749 | register struct ip *ip = mtod(m, struct ip *); | |
750 | register struct mbuf *mb_copy; | |
751 | register struct ip_moptions *imo; | |
752 | register int error; | |
753 | struct ip_moptions simo; | |
754 | ||
755 | mb_copy = m_copy(m, 0, M_COPYALL); | |
756 | if (mb_copy == NULL) | |
757 | return; | |
758 | ||
759 | imo = &simo; | |
760 | imo->imo_multicast_ifp = vifp->v_ifp; | |
761 | imo->imo_multicast_ttl = ip->ip_ttl - 1; | |
762 | imo->imo_multicast_loop = 1; | |
763 | ||
764 | error = ip_output(mb_copy, NULL, NULL, IP_FORWARDING, imo); | |
765 | } | |
766 | ||
767 | static void | |
768 | tunnel_send(m, vifp) | |
769 | register struct mbuf *m; | |
770 | register struct vif *vifp; | |
771 | { | |
772 | register struct ip *ip = mtod(m, struct ip *); | |
773 | register struct mbuf *mb_copy, *mb_opts; | |
774 | register struct ip *ip_copy; | |
775 | register int error; | |
776 | register u_char *cp; | |
777 | ||
778 | /* | |
779 | * Make sure that adding the tunnel options won't exceed the | |
780 | * maximum allowed number of option bytes. | |
781 | */ | |
782 | if (ip->ip_hl > (60 - TUNNEL_LEN) >> 2) { | |
783 | mrtstat.mrts_cant_tunnel++; | |
784 | return; | |
785 | } | |
786 | ||
69d96ae2 AC |
787 | /* |
788 | * Get a private copy of the IP header so that changes to some | |
789 | * of the IP fields don't damage the original header, which is | |
790 | * examined later in ip_input.c. | |
791 | */ | |
792 | mb_copy = m_copy(m, IP_HDR_LEN, M_COPYALL); | |
cf040064 KS |
793 | if (mb_copy == NULL) |
794 | return; | |
cf040064 KS |
795 | MGETHDR(mb_opts, M_DONTWAIT, MT_HEADER); |
796 | if (mb_opts == NULL) { | |
797 | m_freem(mb_copy); | |
798 | return; | |
799 | } | |
cf040064 KS |
800 | /* |
801 | * Make mb_opts be the new head of the packet chain. | |
802 | * Any options of the packet were left in the old packet chain head | |
803 | */ | |
804 | mb_opts->m_next = mb_copy; | |
805 | mb_opts->m_len = IP_HDR_LEN + TUNNEL_LEN; | |
806 | mb_opts->m_data += MSIZE - mb_opts->m_len; | |
69d96ae2 AC |
807 | |
808 | ip_copy = mtod(mb_opts, struct ip *); | |
cf040064 | 809 | /* |
69d96ae2 | 810 | * Copy the base ip header to the new head mbuf. |
cf040064 | 811 | */ |
69d96ae2 AC |
812 | *ip_copy = *ip; |
813 | ip_copy->ip_ttl--; | |
814 | ip_copy->ip_dst = vifp->v_rmt_addr; /* remote tunnel end-point */ | |
815 | /* | |
816 | * Adjust the ip header length to account for the tunnel options. | |
817 | */ | |
818 | ip_copy->ip_hl += TUNNEL_LEN >> 2; | |
819 | ip_copy->ip_len += TUNNEL_LEN; | |
cf040064 KS |
820 | /* |
821 | * Add the NOP and LSRR after the base ip header | |
822 | */ | |
69d96ae2 | 823 | cp = (u_char *)(ip_copy + 1); |
cf040064 KS |
824 | *cp++ = IPOPT_NOP; |
825 | *cp++ = IPOPT_LSRR; | |
826 | *cp++ = 11; /* LSRR option length */ | |
827 | *cp++ = 8; /* LSSR pointer to second element */ | |
828 | *(u_long*)cp = vifp->v_lcl_addr.s_addr; /* local tunnel end-point */ | |
829 | cp += 4; | |
830 | *(u_long*)cp = ip->ip_dst.s_addr; /* destination group */ | |
831 | ||
832 | error = ip_output(mb_opts, NULL, NULL, IP_FORWARDING, NULL); | |
833 | } | |
834 | #endif |