Annotation of embedaddon/pimdd/igmp_proto.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 1998 by the University of Oregon.
3: * All rights reserved.
4: *
5: * Permission to use, copy, modify, and distribute this software and
6: * its documentation in source and binary forms for lawful
7: * purposes and without fee is hereby granted, provided
8: * that the above copyright notice appear in all copies and that both
9: * the copyright notice and this permission notice appear in supporting
10: * documentation, and that any documentation, advertising materials,
11: * and other materials related to such distribution and use acknowledge
12: * that the software was developed by the University of Oregon.
13: * The name of the University of Oregon may not be used to endorse or
14: * promote products derived from this software without specific prior
15: * written permission.
16: *
17: * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
18: * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
19: * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
20: * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
22: * NON-INFRINGEMENT.
23: *
24: * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
25: * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
26: * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
27: * THE USE OR PERFORMANCE OF THIS SOFTWARE.
28: *
29: * Other copyrights might apply to parts of this software and are so
30: * noted when applicable.
31: */
32: /*
33: * Questions concerning this software should be directed to
34: * Kurt Windisch (kurtw@antc.uoregon.edu)
35: *
36: * $Id: igmp_proto.c,v 1.4 1998/05/29 21:58:22 kurtw Exp $
37: */
38: /*
39: * Part of this program has been derived from PIM sparse-mode pimd.
40: * The pimd program is covered by the license in the accompanying file
41: * named "LICENSE.pimd".
42: *
43: * The pimd program is COPYRIGHT 1998 by University of Southern California.
44: *
45: * Part of this program has been derived from mrouted.
46: * The mrouted program is covered by the license in the accompanying file
47: * named "LICENSE.mrouted".
48: *
49: * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
50: * Leland Stanford Junior University.
51: *
52: */
53:
54: #include "defs.h"
55:
56:
57: typedef struct {
58: vifi_t vifi;
59: struct listaddr *g;
60: int q_time;
61: } cbk_t;
62:
63:
64: /*
65: * Forward declarations.
66: */
67: static void DelVif __P((void *arg));
68: static int SetTimer __P((int vifi, struct listaddr *g));
69: static int DeleteTimer __P((int id));
70: static void SendQuery __P((void *arg));
71: static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire,
72: int q_time));
73:
74:
75: /*
76: * Send group membership queries on that interface if I am querier.
77: */
78: void
79: query_groups(v)
80: register struct uvif *v;
81: {
82: register struct listaddr *g;
83:
84: v->uv_gq_timer = IGMP_QUERY_INTERVAL;
85: if (v->uv_flags & VIFF_QUERIER)
86: send_igmp(igmp_send_buf, v->uv_lcl_addr, allhosts_group,
87: IGMP_MEMBERSHIP_QUERY,
88: (v->uv_flags & VIFF_IGMPV1) ? 0 :
89: IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
90: /*
91: * Decrement the old-hosts-present timer for each
92: * active group on that vif.
93: */
94: for (g = v->uv_groups; g != NULL; g = g->al_next)
95: if (g->al_old > TIMER_INTERVAL)
96: g->al_old -= TIMER_INTERVAL;
97: else
98: g->al_old = 0;
99: }
100:
101:
102: /*
103: * Process an incoming host membership query
104: */
105: void
106: accept_membership_query(src, dst, group, tmo)
107: u_int32 src, dst, group;
108: int tmo;
109: {
110: register vifi_t vifi;
111: register struct uvif *v;
112:
113: /* Ignore my own membership query */
114: if (local_address(src) != NO_VIF)
115: return;
116:
117: /* TODO: modify for DVMRP?? */
118: if ((vifi = find_vif_direct(src)) == NO_VIF) {
119: IF_DEBUG(DEBUG_IGMP)
120: log(LOG_INFO, 0,
121: "ignoring group membership query from non-adjacent host %s",
122: inet_fmt(src, s1));
123: return;
124: }
125:
126: v = &uvifs[vifi];
127:
128: if ((tmo == 0 && !(v->uv_flags & VIFF_IGMPV1)) ||
129: (tmo != 0 && (v->uv_flags & VIFF_IGMPV1))) {
130: int i;
131:
132: /*
133: * Exponentially back-off warning rate
134: */
135: i = ++v->uv_igmpv1_warn;
136: while (i && !(i & 1))
137: i >>= 1;
138: if (i == 1)
139: log(LOG_WARNING, 0, "%s %s on vif %d, %s",
140: tmo == 0 ? "Received IGMPv1 report from"
141: : "Received IGMPv2 report from",
142: inet_fmt(src, s1),
143: vifi,
144: tmo == 0 ? "please configure vif for IGMPv1"
145: : "but I am configured for IGMPv1");
146: }
147:
148: if (v->uv_querier == NULL || v->uv_querier->al_addr != src) {
149: /*
150: * This might be:
151: * - A query from a new querier, with a lower source address
152: * than the current querier (who might be me)
153: * - A query from a new router that just started up and doesn't
154: * know who the querier is.
155: * - A query from the current querier
156: */
157: if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr)
158: : ntohl(v->uv_lcl_addr))) {
159: IF_DEBUG(DEBUG_IGMP)
160: log(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
161: inet_fmt(src, s1),
162: v->uv_querier ?
163: inet_fmt(v->uv_querier->al_addr, s2) :
164: "me", vifi);
165: if (!v->uv_querier) {
166: v->uv_querier = (struct listaddr *)
167: malloc(sizeof(struct listaddr));
168: v->uv_querier->al_next = (struct listaddr *)NULL;
169: v->uv_querier->al_timer = 0;
170: v->uv_querier->al_genid = 0;
171: /* TODO: write the protocol version */
172: v->uv_querier->al_pv = 0;
173: v->uv_querier->al_mv = 0;
174: v->uv_querier->al_old = 0;
175: v->uv_querier->al_index = 0;
176: v->uv_querier->al_timerid = 0;
177: v->uv_querier->al_query = 0;
178: v->uv_querier->al_flags = 0;
179:
180: v->uv_flags &= ~VIFF_QUERIER;
181: }
182: v->uv_querier->al_addr = src;
183: time(&v->uv_querier->al_ctime);
184: }
185: }
186:
187: /*
188: * Reset the timer since we've received a query.
189: */
190: if (v->uv_querier && src == v->uv_querier->al_addr)
191: v->uv_querier->al_timer = 0;
192:
193: /*
194: * If this is a Group-Specific query which we did not source,
195: * we must set our membership timer to [Last Member Query Count] *
196: * the [Max Response Time] in the packet.
197: */
198: if (!(v->uv_flags & VIFF_IGMPV1) && group != 0 &&
199: src != v->uv_lcl_addr) {
200: register struct listaddr *g;
201:
202: IF_DEBUG(DEBUG_IGMP)
203: log(LOG_DEBUG, 0,
204: "%s for %s from %s on vif %d, timer %d",
205: "Group-specific membership query",
206: inet_fmt(group, s2), inet_fmt(src, s1), vifi, tmo);
207:
208: for (g = v->uv_groups; g != NULL; g = g->al_next) {
209: if (group == g->al_addr && g->al_query == 0) {
210: /* setup a timeout to remove the group membership */
211: if (g->al_timerid)
212: g->al_timerid = DeleteTimer(g->al_timerid);
213: g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT *
214: tmo / IGMP_TIMER_SCALE;
215: /* use al_query to record our presence in last-member state */
216: g->al_query = -1;
217: g->al_timerid = SetTimer(vifi, g);
218: IF_DEBUG(DEBUG_IGMP)
219: log(LOG_DEBUG, 0,
220: "timer for grp %s on vif %d set to %d",
221: inet_fmt(group, s2), vifi, g->al_timer);
222: break;
223: }
224: }
225: }
226: }
227:
228:
229: /*
230: * Process an incoming group membership report.
231: */
232: void
233: accept_group_report(src, dst, group, igmp_report_type)
234: u_int32 src, dst, group;
235: int igmp_report_type;
236: {
237: register vifi_t vifi;
238: register struct uvif *v;
239: register struct listaddr *g;
240:
241: if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
242: IF_DEBUG(DEBUG_IGMP)
243: log(LOG_INFO, 0,
244: "ignoring group membership report from non-adjacent host %s",
245: inet_fmt(src, s1));
246: return;
247: }
248:
249: IF_DEBUG(DEBUG_IGMP)
250: log(LOG_INFO, 0,
251: "accepting IGMP group membership report: src %s, dst% s, grp %s",
252: inet_fmt(src, s1), inet_fmt(dst, s2), inet_fmt(group, s3));
253:
254: v = &uvifs[vifi];
255:
256: /*
257: * Look for the group in our group list; if found, reset its timer.
258: */
259: for (g = v->uv_groups; g != NULL; g = g->al_next) {
260: if (group == g->al_addr) {
261: if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT)
262: g->al_old = DVMRP_OLD_AGE_THRESHOLD;
263:
264: g->al_reporter = src;
265:
266: /** delete old timers, set a timer for expiration **/
267: g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
268: if (g->al_query)
269: g->al_query = DeleteTimer(g->al_query);
270: if (g->al_timerid)
271: g->al_timerid = DeleteTimer(g->al_timerid);
272: g->al_timerid = SetTimer(vifi, g);
273: add_leaf(vifi, INADDR_ANY_N, group);
274: break;
275: }
276: }
277:
278: /*
279: * If not found, add it to the list and update kernel cache.
280: */
281: if (g == NULL) {
282: g = (struct listaddr *)malloc(sizeof(struct listaddr));
283: if (g == NULL)
284: log(LOG_ERR, 0, "ran out of memory"); /* fatal */
285:
286: g->al_addr = group;
287: if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT)
288: g->al_old = DVMRP_OLD_AGE_THRESHOLD;
289: else
290: g->al_old = 0;
291:
292: /** set a timer for expiration **/
293: g->al_query = 0;
294: g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
295: g->al_reporter = src;
296: g->al_timerid = SetTimer(vifi, g);
297: g->al_next = v->uv_groups;
298: v->uv_groups = g;
299: time(&g->al_ctime);
300:
301: add_leaf(vifi, INADDR_ANY_N, group);
302: }
303: }
304:
305:
306: /* TODO: send PIM prune message if the last member? */
307: void
308: accept_leave_message(src, dst, group)
309: u_int32 src, dst, group;
310: {
311: register vifi_t vifi;
312: register struct uvif *v;
313: register struct listaddr *g;
314:
315: /* TODO: modify for DVMRP ??? */
316: if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
317: IF_DEBUG(DEBUG_IGMP)
318: log(LOG_INFO, 0,
319: "ignoring group leave report from non-adjacent host %s",
320: inet_fmt(src, s1));
321: return;
322: }
323:
324: IF_DEBUG(DEBUG_IGMP)
325: log(LOG_INFO, 0,
326: "accepting IGMP leave message: src %s, dst% s, grp %s",
327: inet_fmt(src, s1), inet_fmt(dst, s2), inet_fmt(group, s3));
328:
329: v = &uvifs[vifi];
330:
331: if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR))
332: || (v->uv_flags & VIFF_IGMPV1))
333: return;
334:
335: /*
336: * Look for the group in our group list in order to set up a short-timeout
337: * query.
338: */
339: for (g = v->uv_groups; g != NULL; g = g->al_next) {
340: if (group == g->al_addr) {
341: IF_DEBUG(DEBUG_IGMP)
342: log(LOG_DEBUG, 0,
343: "[vif.c, _accept_leave_message] %d %d \n",
344: g->al_old, g->al_query);
345:
346: /* Ignore the leave message if there are old hosts present */
347: if (g->al_old)
348: return;
349:
350: /* still waiting for a reply to a query, ignore the leave */
351: if (g->al_query)
352: return;
353:
354: /** delete old timer set a timer for expiration **/
355: if (g->al_timerid)
356: g->al_timerid = DeleteTimer(g->al_timerid);
357:
358: #if IGMP_LAST_MEMBER_QUERY_COUNT != 2
359: /*
360: This code needs to be updated to keep a counter of the number
361: of queries remaining.
362: */
363: #endif
364: /** send a group specific querry **/
365: g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL *
366: (IGMP_LAST_MEMBER_QUERY_COUNT + 1);
367: if (v->uv_flags & VIFF_QUERIER)
368: send_igmp(igmp_send_buf, v->uv_lcl_addr, g->al_addr,
369: IGMP_MEMBERSHIP_QUERY,
370: IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE,
371: g->al_addr, 0);
372: g->al_query = SetQueryTimer(g, vifi,
373: IGMP_LAST_MEMBER_QUERY_INTERVAL,
374: IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE);
375: g->al_timerid = SetTimer(vifi, g);
376: break;
377: }
378: }
379: }
380:
381:
382: /*
383: * Time out record of a group membership on a vif
384: */
385: static void
386: DelVif(arg)
387: void *arg;
388: {
389: cbk_t *cbk = (cbk_t *)arg;
390: vifi_t vifi = cbk->vifi;
391: struct uvif *v = &uvifs[vifi];
392: struct listaddr *a, **anp, *g = cbk->g;
393:
394: /*
395: * Group has expired
396: * delete all kernel cache entries with this group
397: */
398: if (g->al_query)
399: DeleteTimer(g->al_query);
400:
401: delete_leaf(vifi, INADDR_ANY_N, g->al_addr);
402:
403: anp = &(v->uv_groups);
404: while ((a = *anp) != NULL) {
405: if (a == g) {
406: *anp = a->al_next;
407: free((char *)a);
408: } else {
409: anp = &a->al_next;
410: }
411: }
412:
413: free(cbk);
414: }
415:
416:
417: /*
418: * Set a timer to delete the record of a group membership on a vif.
419: */
420: static int
421: SetTimer(vifi, g)
422: vifi_t vifi;
423: struct listaddr *g;
424: {
425: cbk_t *cbk;
426:
427: cbk = (cbk_t *) malloc(sizeof(cbk_t));
428: cbk->vifi = vifi;
429: cbk->g = g;
430: return timer_setTimer(g->al_timer, DelVif, cbk);
431: }
432:
433:
434: /*
435: * Delete a timer that was set above.
436: */
437: static int
438: DeleteTimer(id)
439: int id;
440: {
441: timer_clearTimer(id);
442: return 0;
443: }
444:
445:
446: /*
447: * Send a group-specific query.
448: */
449: static void
450: SendQuery(arg)
451: void *arg;
452: {
453: cbk_t *cbk = (cbk_t *)arg;
454: register struct uvif *v = &uvifs[cbk->vifi];
455:
456: if (v->uv_flags & VIFF_QUERIER)
457: send_igmp(igmp_send_buf, v->uv_lcl_addr, cbk->g->al_addr,
458: IGMP_MEMBERSHIP_QUERY,
459: cbk->q_time, cbk->g->al_addr, 0);
460: cbk->g->al_query = 0;
461: free(cbk);
462: }
463:
464:
465: /*
466: * Set a timer to send a group-specific query.
467: */
468: static int
469: SetQueryTimer(g, vifi, to_expire, q_time)
470: struct listaddr *g;
471: vifi_t vifi;
472: int to_expire;
473: int q_time;
474: {
475: cbk_t *cbk;
476:
477: cbk = (cbk_t *) malloc(sizeof(cbk_t));
478: cbk->g = g;
479: cbk->q_time = q_time;
480: cbk->vifi = vifi;
481: return timer_setTimer(to_expire, SendQuery, cbk);
482: }
483:
484: /* Checks for IGMP group membership: returns TRUE if there is a receiver for the
485: * group on the given vif, or returns FALSE otherwise.
486: */
487: int check_grp_membership(v, group)
488: struct uvif *v;
489: u_int32 group;
490: {
491: register struct listaddr *g;
492:
493: /*
494: * Look for the group in our group list;
495: */
496: for (g = v->uv_groups; g != NULL; g = g->al_next) {
497: if (group == g->al_addr)
498: return TRUE;
499: }
500: return FALSE;
501: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>