File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mrouted / igmp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:10:48 2012 UTC (12 years, 3 months ago) by misho
Branches: mrouted, MAIN
CVS tags: v3_9_6p0, v3_9_6, v3_9_5, HEAD
mrouted

    1: /*
    2:  * The mrouted program is covered by the license in the accompanying file
    3:  * named "LICENSE".  Use of the mrouted program represents acceptance of
    4:  * the terms and conditions listed in that file.
    5:  *
    6:  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
    7:  * Leland Stanford Junior University.
    8:  */
    9: 
   10: #include "defs.h"
   11: 
   12: /*
   13:  * Exported variables.
   14:  */
   15: char		*recv_buf; 		     /* input packet buffer         */
   16: char		*send_buf; 		     /* output packet buffer        */
   17: int		igmp_socket;		     /* socket for all network I/O  */
   18: u_int32		allhosts_group;		     /* All hosts addr in net order */
   19: u_int32		allrtrs_group;		     /* All-Routers "  in net order */
   20: u_int32		dvmrp_group;		     /* DVMRP grp addr in net order */
   21: u_int32		dvmrp_genid;		     /* IGMP generation id          */
   22: 
   23: /*
   24:  * Local function definitions.
   25:  */
   26: /* u_char promoted to u_int */
   27: static int	igmp_log_level(u_int type, u_int code);
   28: 
   29: /*
   30:  * Open and initialize the igmp socket, and fill in the non-changing
   31:  * IP header fields in the output packet buffer.
   32:  */
   33: void init_igmp(void)
   34: {
   35:     struct ip *ip;
   36: 
   37:     recv_buf = malloc(RECV_BUF_SIZE);
   38:     send_buf = malloc(RECV_BUF_SIZE);
   39: 
   40:     if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 
   41: 	logit(LOG_ERR, errno, "IGMP socket");
   42: 
   43:     k_hdr_include(TRUE);	/* include IP header when sending */
   44:     k_set_rcvbuf(256*1024,48*1024);	/* lots of input buffering        */
   45:     k_set_ttl(1);		/* restrict multicasts to one hop */
   46:     k_set_loop(FALSE);		/* disable multicast loopback     */
   47: 
   48:     ip         = (struct ip *)send_buf;
   49:     memset(ip, 0, sizeof(struct ip));
   50:     /*
   51:      * Fields zeroed that aren't filled in later:
   52:      * - IP ID (let the kernel fill it in)
   53:      * - Offset (we don't send fragments)
   54:      * - Checksum (let the kernel fill it in)
   55:      */
   56:     ip->ip_v   = IPVERSION;
   57:     ip->ip_hl  = sizeof(struct ip) >> 2;
   58:     ip->ip_tos = 0xc0;		/* Internet Control */
   59:     ip->ip_ttl = MAXTTL;	/* applies to unicasts only */
   60:     ip->ip_p   = IPPROTO_IGMP;
   61: 
   62:     allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
   63:     dvmrp_group    = htonl(INADDR_DVMRP_GROUP);
   64:     allrtrs_group  = htonl(INADDR_ALLRTRS_GROUP);
   65: }
   66: 
   67: #define PIM_QUERY        0
   68: #define PIM_REGISTER     1
   69: #define PIM_REGISTER_STOP 	2
   70: #define PIM_JOIN_PRUNE   3
   71: #define PIM_RP_REACHABLE 4
   72: #define PIM_ASSERT       5
   73: #define PIM_GRAFT        6
   74: #define PIM_GRAFT_ACK    7
   75: 
   76: char *igmp_packet_kind(u_int type, u_int code)
   77: {
   78:     static char unknown[20];
   79: 
   80:     switch (type) {
   81: 	case IGMP_MEMBERSHIP_QUERY:		return "membership query  ";
   82: 	case IGMP_V1_MEMBERSHIP_REPORT:		return "V1 member report  ";
   83: 	case IGMP_V2_MEMBERSHIP_REPORT:		return "V2 member report  ";
   84: 	case IGMP_V2_LEAVE_GROUP:		return "leave message     ";
   85: 	case IGMP_DVMRP:
   86: 	  switch (code) {
   87: 	    case DVMRP_PROBE:			return "neighbor probe    ";
   88: 	    case DVMRP_REPORT:			return "route report      ";
   89: 	    case DVMRP_ASK_NEIGHBORS:		return "neighbor request  ";
   90: 	    case DVMRP_NEIGHBORS:		return "neighbor list     ";
   91: 	    case DVMRP_ASK_NEIGHBORS2:		return "neighbor request 2";
   92: 	    case DVMRP_NEIGHBORS2:		return "neighbor list 2   ";
   93: 	    case DVMRP_PRUNE:			return "prune message     ";
   94: 	    case DVMRP_GRAFT:			return "graft message     ";
   95: 	    case DVMRP_GRAFT_ACK:		return "graft message ack ";
   96: 	    case DVMRP_INFO_REQUEST:		return "info request      ";
   97: 	    case DVMRP_INFO_REPLY:		return "info reply        ";
   98: 	    default:
   99: 		    snprintf(unknown, sizeof(unknown), "unknown DVMRP %3d ", code);
  100: 		    return unknown;
  101: 	  }
  102:  	case IGMP_PIM:
  103:  	  switch (code) {
  104:  	    case PIM_QUERY:			return "PIM Router-Query  ";
  105:  	    case PIM_REGISTER:			return "PIM Register      ";
  106:  	    case PIM_REGISTER_STOP:		return "PIM Register-Stop ";
  107:  	    case PIM_JOIN_PRUNE:		return "PIM Join/Prune    ";
  108:  	    case PIM_RP_REACHABLE:		return "PIM RP-Reachable  ";
  109:  	    case PIM_ASSERT:			return "PIM Assert        ";
  110:  	    case PIM_GRAFT:			return "PIM Graft         ";
  111:  	    case PIM_GRAFT_ACK:			return "PIM Graft-Ack     ";
  112:  	    default:
  113:  		    snprintf(unknown, sizeof(unknown), "unknown PIM msg%3d", code);
  114: 		    return unknown;
  115:  	  }
  116: 	case IGMP_MTRACE:			return "IGMP trace query  ";
  117: 	case IGMP_MTRACE_RESP:			return "IGMP trace reply  ";
  118: 	default:
  119: 		snprintf(unknown, sizeof(unknown), "unk: 0x%02x/0x%02x    ", type, code);
  120: 		return unknown;
  121:     }
  122: }
  123: 
  124: int igmp_debug_kind(u_int type, u_int code)
  125: {
  126:     switch (type) {
  127: 	case IGMP_MEMBERSHIP_QUERY:		return DEBUG_IGMP;
  128: 	case IGMP_V1_MEMBERSHIP_REPORT:		return DEBUG_IGMP;
  129: 	case IGMP_V2_MEMBERSHIP_REPORT:		return DEBUG_IGMP;
  130: 	case IGMP_V2_LEAVE_GROUP:		return DEBUG_IGMP;
  131: 	case IGMP_DVMRP:
  132: 	  switch (code) {
  133: 	    case DVMRP_PROBE:			return DEBUG_PEER;
  134: 	    case DVMRP_REPORT:			return DEBUG_ROUTE;
  135: 	    case DVMRP_ASK_NEIGHBORS:		return 0;
  136: 	    case DVMRP_NEIGHBORS:		return 0;
  137: 	    case DVMRP_ASK_NEIGHBORS2:		return 0;
  138: 	    case DVMRP_NEIGHBORS2:		return 0;
  139: 	    case DVMRP_PRUNE:			return DEBUG_PRUNE;
  140: 	    case DVMRP_GRAFT:			return DEBUG_PRUNE;
  141: 	    case DVMRP_GRAFT_ACK:		return DEBUG_PRUNE;
  142: 	    case DVMRP_INFO_REQUEST:		return 0;
  143: 	    case DVMRP_INFO_REPLY:		return 0;
  144: 	    default:				return 0;
  145: 	  }
  146: 	case IGMP_PIM:
  147: 	  switch (code) {
  148: 	    case PIM_QUERY:			return 0;
  149: 	    case PIM_REGISTER:			return 0;
  150: 	    case PIM_REGISTER_STOP:		return 0;
  151: 	    case PIM_JOIN_PRUNE:		return 0;
  152: 	    case PIM_RP_REACHABLE:		return 0;
  153: 	    case PIM_ASSERT:			return 0;
  154: 	    case PIM_GRAFT:			return 0;
  155: 	    case PIM_GRAFT_ACK:			return 0;
  156: 	    default:				return 0;
  157: 	  }
  158: 	case IGMP_MTRACE:			return DEBUG_TRACE;
  159: 	case IGMP_MTRACE_RESP:			return DEBUG_TRACE;
  160: 	default:				return DEBUG_IGMP;
  161:     }
  162: }
  163: 
  164: /*
  165:  * Process a newly received IGMP packet that is sitting in the input
  166:  * packet buffer.
  167:  */
  168: void accept_igmp(size_t recvlen)
  169: {
  170:     register u_int32 src, dst, group;
  171:     struct ip *ip;
  172:     struct igmp *igmp;
  173:     int ipdatalen, iphdrlen, igmpdatalen;
  174: 
  175:     if (recvlen < sizeof(struct ip)) {
  176: 	logit(LOG_WARNING, 0,
  177: 	    "received packet too short (%u bytes) for IP header", recvlen);
  178: 	return;
  179:     }
  180: 
  181:     ip        = (struct ip *)recv_buf;
  182:     src       = ip->ip_src.s_addr;
  183:     dst       = ip->ip_dst.s_addr;
  184: 
  185:     /* 
  186:      * this is most likely a message from the kernel indicating that
  187:      * a new src grp pair message has arrived and so, it would be 
  188:      * necessary to install a route into the kernel for this.
  189:      */
  190:     if (ip->ip_p == 0) {
  191: 	if (src == 0 || dst == 0)
  192: 	    logit(LOG_WARNING, 0, "kernel request not accurate");
  193: 	else
  194: 	    add_table_entry(src, dst);
  195: 	return;
  196:     }
  197: 
  198:     iphdrlen  = ip->ip_hl << 2;
  199:     ipdatalen = ntohs(ip->ip_len) - iphdrlen;
  200:     if ((size_t)(iphdrlen + ipdatalen) != recvlen) {
  201: 	logit(LOG_WARNING, 0,
  202: 	    "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
  203: 	    inet_fmt(src, s1, sizeof(s1)), recvlen, iphdrlen, ipdatalen);
  204: 	return;
  205:     }
  206: 
  207:     igmp        = (struct igmp *)(recv_buf + iphdrlen);
  208:     group       = igmp->igmp_group.s_addr;
  209:     igmpdatalen = ipdatalen - IGMP_MINLEN;
  210:     if (igmpdatalen < 0) {
  211: 	logit(LOG_WARNING, 0,
  212: 	    "received IP data field too short (%u bytes) for IGMP, from %s",
  213: 	    ipdatalen, inet_fmt(src, s1, sizeof(s1)));
  214: 	return;
  215:     }
  216: 
  217:     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
  218:     logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
  219: 	igmp_packet_kind(igmp->igmp_type, igmp->igmp_code),
  220: 	inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
  221: 
  222:     switch (igmp->igmp_type) {
  223: 
  224: 	case IGMP_MEMBERSHIP_QUERY:
  225: 	    accept_membership_query(src, dst, group, igmp->igmp_code);
  226: 	    return;
  227: 
  228: 	case IGMP_V1_MEMBERSHIP_REPORT:
  229: 	case IGMP_V2_MEMBERSHIP_REPORT:
  230: 	    accept_group_report(src, dst, group, igmp->igmp_type);
  231: 	    return;
  232: 	    
  233: 	case IGMP_V2_LEAVE_GROUP:
  234: 	    accept_leave_message(src, dst, group);
  235: 	    return;
  236: 
  237: 	case IGMP_DVMRP:
  238: 	    group = ntohl(group);
  239: 
  240: 	    switch (igmp->igmp_code) {
  241: 		case DVMRP_PROBE:
  242: 		    accept_probe(src, dst, (char *)(igmp+1), igmpdatalen, group);
  243: 		    return;
  244: 
  245: 		case DVMRP_REPORT:
  246: 		    accept_report(src, dst, (char *)(igmp+1), igmpdatalen, group);
  247: 		    return;
  248: 
  249: 		case DVMRP_ASK_NEIGHBORS:
  250: 		    accept_neighbor_request(src, dst);
  251: 		    return;
  252: 
  253: 		case DVMRP_ASK_NEIGHBORS2:
  254: 		    accept_neighbor_request2(src, dst);
  255: 		    return;
  256: 
  257: 		case DVMRP_NEIGHBORS:
  258: 		    accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen, group);
  259: 		    return;
  260: 
  261: 		case DVMRP_NEIGHBORS2:
  262: 		    accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen, group);
  263: 		    return;
  264: 
  265: 		case DVMRP_PRUNE:
  266: 		    accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
  267: 		    return;
  268: 
  269: 		case DVMRP_GRAFT:
  270: 		    accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
  271: 		    return;
  272: 
  273: 		case DVMRP_GRAFT_ACK:
  274: 		    accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
  275: 		    return;
  276: 
  277: 		case DVMRP_INFO_REQUEST:
  278: 		    accept_info_request(src, dst, (u_char *)(igmp+1), igmpdatalen);
  279: 		    return;
  280: 
  281: 		case DVMRP_INFO_REPLY:
  282: 		    accept_info_reply(src, dst, (u_char *)(igmp+1), igmpdatalen);
  283: 		    return;
  284: 
  285: 		default:
  286: 		    logit(LOG_INFO, 0,
  287: 		     "ignoring unknown DVMRP message code %u from %s to %s",
  288: 		     igmp->igmp_code, inet_fmt(src, s1, sizeof(s1)),
  289: 		     inet_fmt(dst, s2, sizeof(s2)));
  290: 		    return;
  291: 	    }
  292: 
  293:  	case IGMP_PIM:
  294:  	    return;
  295: 
  296: 	case IGMP_MTRACE_RESP:
  297: 	    return;
  298: 
  299: 	case IGMP_MTRACE:
  300: 	    accept_mtrace(src, dst, group, (char *)(igmp+1),
  301: 		   igmp->igmp_code, igmpdatalen);
  302: 	    return;
  303: 
  304: 	default:
  305: 	    logit(LOG_INFO, 0,
  306: 		"ignoring unknown IGMP message type %x from %s to %s",
  307: 		igmp->igmp_type, inet_fmt(src, s1, sizeof(s1)),
  308: 		inet_fmt(dst, s2, sizeof(s2)));
  309: 	    return;
  310:     }
  311: }
  312: 
  313: /*
  314:  * Some IGMP messages are more important than others.  This routine
  315:  * determines the logging level at which to log a send error (often
  316:  * "No route to host").  This is important when there is asymmetric
  317:  * reachability and someone is trying to, i.e., mrinfo me periodically.
  318:  */
  319: static int igmp_log_level(u_int type, u_int code)
  320: {
  321:     switch (type) {
  322: 	case IGMP_MTRACE_RESP:
  323: 	    return LOG_INFO;
  324: 
  325: 	case IGMP_DVMRP:
  326: 	  switch (code) {
  327: 	    case DVMRP_NEIGHBORS:
  328: 	    case DVMRP_NEIGHBORS2:
  329: 		return LOG_INFO;
  330: 	  }
  331:     }
  332:     return LOG_WARNING;
  333: }
  334: 
  335: /*
  336:  * Construct an IGMP message in the output packet buffer.  The caller may
  337:  * have already placed data in that buffer, of length 'datalen'.
  338:  */
  339: size_t build_igmp(u_int32 src, u_int32 dst, int type, int code, u_int32 group, int datalen)
  340: {
  341:     struct ip *ip;
  342:     struct igmp *igmp;
  343:     extern int curttl;
  344:     size_t len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
  345: 
  346:     ip                      = (struct ip *)send_buf;
  347:     ip->ip_src.s_addr       = src;
  348:     ip->ip_dst.s_addr       = dst;
  349:     ip->ip_len              = htons(len);
  350:     if (IN_MULTICAST(ntohl(dst))) {
  351: 	ip->ip_ttl = curttl;
  352:     } else {
  353: 	ip->ip_ttl = MAXTTL;
  354:     }
  355: 
  356:     igmp                    = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
  357:     igmp->igmp_type         = type;
  358:     igmp->igmp_code         = code;
  359:     igmp->igmp_group.s_addr = group;
  360:     igmp->igmp_cksum        = 0;
  361:     igmp->igmp_cksum        = inet_cksum((u_int16_t *)igmp,
  362: 					 IGMP_MINLEN + datalen);
  363: 
  364:     return len;
  365: }
  366: 
  367: /* 
  368:  * Call build_igmp() to build an IGMP message in the output packet buffer.
  369:  * Then send the message from the interface with IP address 'src' to
  370:  * destination 'dst'.
  371:  */
  372: void send_igmp(u_int32 src, u_int32 dst, int type, int code, u_int32 group, int datalen)
  373: {
  374:     struct sockaddr_in sdst;
  375:     int setloop = 0;
  376:     size_t len;
  377: 
  378:     len = build_igmp(src, dst, type, code, group, datalen);
  379: 
  380:     if (IN_MULTICAST(ntohl(dst))) {
  381: 	k_set_if(src);
  382: 	if (type != IGMP_DVMRP || dst == allhosts_group) {
  383: 	    setloop = 1;
  384: 	    k_set_loop(TRUE);
  385: 	}
  386:     }
  387: 
  388:     memset(&sdst, 0, sizeof(sdst));
  389:     sdst.sin_family = AF_INET;
  390: #ifdef HAVE_SA_LEN
  391:     sdst.sin_len = sizeof(sdst);
  392: #endif
  393:     sdst.sin_addr.s_addr = dst;
  394:     if (sendto(igmp_socket, send_buf, len, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
  395: 	if (errno == ENETDOWN)
  396: 	    check_vif_state();
  397: 	else
  398: 	    logit(igmp_log_level(type, code), errno,
  399: 		"sendto to %s on %s",
  400: 		inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2)));
  401:     }
  402: 
  403:     if (setloop)
  404: 	    k_set_loop(FALSE);
  405: 
  406:     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
  407:     logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
  408: 	igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
  409: 				 inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
  410: }
  411: 
  412: /**
  413:  * Local Variables:
  414:  *  version-control: t
  415:  *  indent-tabs-mode: t
  416:  *  c-file-style: "ellemtel"
  417:  *  c-basic-offset: 4
  418:  * End:
  419:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>