Home | History | Annotate | Line # | Download | only in mrouted
igmp.c revision 1.4
      1 /*	$NetBSD: igmp.c,v 1.4 1995/10/09 03:51:40 thorpej Exp $	*/
      2 
      3 /*
      4  * The mrouted program is covered by the license in the accompanying file
      5  * named "LICENSE".  Use of the mrouted program represents acceptance of
      6  * the terms and conditions listed in that file.
      7  *
      8  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
      9  * Leland Stanford Junior University.
     10  */
     11 
     12 
     13 #include "defs.h"
     14 
     15 
     16 /*
     17  * Exported variables.
     18  */
     19 char		*recv_buf; 		     /* input packet buffer         */
     20 char		*send_buf; 		     /* output packet buffer        */
     21 int		igmp_socket;		     /* socket for all network I/O  */
     22 u_int32_t	allhosts_group;		     /* All hosts addr in net order */
     23 u_int32_t	allrtrs_group;		     /* All-Routers "  in net order */
     24 u_int32_t	dvmrp_group;		     /* DVMRP grp addr in net order */
     25 u_int32_t	dvmrp_genid;		     /* IGMP generation id          */
     26 
     27 /*
     28  * Open and initialize the igmp socket, and fill in the non-changing
     29  * IP header fields in the output packet buffer.
     30  */
     31 void init_igmp()
     32 {
     33     struct ip *ip;
     34 
     35     recv_buf = malloc(RECV_BUF_SIZE);
     36     send_buf = malloc(RECV_BUF_SIZE);
     37 
     38     if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
     39 	log(LOG_ERR, errno, "IGMP socket");
     40 
     41     k_hdr_include(TRUE);	/* include IP header when sending */
     42     k_set_rcvbuf(48*1024);	/* lots of input buffering        */
     43     k_set_ttl(1);		/* restrict multicasts to one hop */
     44     k_set_loop(FALSE);		/* disable multicast loopback     */
     45 
     46     ip         = (struct ip *)send_buf;
     47     ip->ip_hl  = sizeof(struct ip) >> 2;
     48     ip->ip_v   = IPVERSION;
     49     ip->ip_tos = 0;
     50     ip->ip_off = 0;
     51     ip->ip_p   = IPPROTO_IGMP;
     52     ip->ip_ttl = MAXTTL;	/* applies to unicasts only */
     53 
     54     allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
     55     dvmrp_group    = htonl(INADDR_DVMRP_GROUP);
     56     allrtrs_group  = htonl(INADDR_ALLRTRS_GROUP);
     57 }
     58 
     59 #define PIM_QUERY        0
     60 #define PIM_REGISTER     1
     61 #define PIM_REGISTER_STOP 	2
     62 #define PIM_JOIN_PRUNE   3
     63 #define PIM_RP_REACHABLE 4
     64 #define PIM_ASSERT       5
     65 #define PIM_GRAFT        6
     66 #define PIM_GRAFT_ACK    7
     67 
     68 static char *packet_kind(type, code)
     69      u_char type, code;
     70 {
     71     switch (type) {
     72 	case IGMP_HOST_MEMBERSHIP_QUERY:	return "membership query  ";
     73 	case IGMP_v1_HOST_MEMBERSHIP_REPORT:	return "membership report ";
     74 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:	return "new member report ";
     75 	case IGMP_HOST_LEAVE_MESSAGE:           return "leave message     ";
     76 	case IGMP_DVMRP:
     77 	  switch (code) {
     78 	    case DVMRP_PROBE:	    		return "neighbor probe    ";
     79 	    case DVMRP_REPORT:	    		return "route report      ";
     80 	    case DVMRP_ASK_NEIGHBORS:   	return "neighbor request  ";
     81 	    case DVMRP_NEIGHBORS:	    	return "neighbor list     ";
     82 	    case DVMRP_ASK_NEIGHBORS2:   	return "neighbor request 2";
     83 	    case DVMRP_NEIGHBORS2:	    	return "neighbor list 2   ";
     84 	    case DVMRP_PRUNE:			return "prune message     ";
     85 	    case DVMRP_GRAFT:			return "graft message     ";
     86 	    case DVMRP_GRAFT_ACK:		return "graft message ack ";
     87 	    default:	    			return "unknown DVMRP msg ";
     88 	  }
     89  	case IGMP_PIM:
     90  	  switch (code) {
     91  	    case PIM_QUERY:			return "PIM Router-Query  ";
     92  	    case PIM_REGISTER:			return "PIM Register      ";
     93  	    case PIM_REGISTER_STOP:		return "PIM Register-Stop ";
     94  	    case PIM_JOIN_PRUNE:		return "PIM Join/Prune    ";
     95  	    case PIM_RP_REACHABLE:		return "PIM RP-Reachable  ";
     96  	    case PIM_ASSERT:			return "PIM Assert        ";
     97  	    case PIM_GRAFT:			return "PIM Graft         ";
     98  	    case PIM_GRAFT_ACK:			return "PIM Graft-Ack     ";
     99  	    default:		    		return "unknown PIM msg   ";
    100  	  }
    101 	case IGMP_MTRACE_QUERY:			return "IGMP trace query  ";
    102 	case IGMP_MTRACE_REPLY:			return "IGMP trace reply  ";
    103 	default:			    	return "unknown IGMP msg  ";
    104     }
    105 }
    106 
    107 /*
    108  * Process a newly received IGMP packet that is sitting in the input
    109  * packet buffer.
    110  */
    111 void accept_igmp(recvlen)
    112     int recvlen;
    113 {
    114     register u_int32_t src, dst, group;
    115     struct ip *ip;
    116     struct igmp *igmp;
    117     int ipdatalen, iphdrlen, igmpdatalen;
    118 
    119     if (recvlen < sizeof(struct ip)) {
    120 	log(LOG_WARNING, 0,
    121 	    "received packet too short (%u bytes) for IP header", recvlen);
    122 	return;
    123     }
    124 
    125     ip        = (struct ip *)recv_buf;
    126     src       = ip->ip_src.s_addr;
    127     dst       = ip->ip_dst.s_addr;
    128 
    129     /*
    130      * this is most likely a message from the kernel indicating that
    131      * a new src grp pair message has arrived and so, it would be
    132      * necessary to install a route into the kernel for this.
    133      */
    134     if (ip->ip_p == 0) {
    135 	if (src == 0 || dst == 0)
    136 	    log(LOG_WARNING, 0, "kernel request not accurate");
    137 	else
    138 	    add_table_entry(src, dst);
    139 	return;
    140     }
    141 
    142     iphdrlen  = ip->ip_hl << 2;
    143     ipdatalen = ip->ip_len;
    144     if (iphdrlen + ipdatalen != recvlen) {
    145 	log(LOG_WARNING, 0,
    146 	    "received packet shorter (%u bytes) than hdr+data length (%u+%u)",
    147 	    recvlen, iphdrlen, ipdatalen);
    148 	return;
    149     }
    150 
    151     igmp        = (struct igmp *)(recv_buf + iphdrlen);
    152     group       = igmp->igmp_group.s_addr;
    153     igmpdatalen = ipdatalen - IGMP_MINLEN;
    154     if (igmpdatalen < 0) {
    155 	log(LOG_WARNING, 0,
    156 	    "received IP data field too short (%u bytes) for IGMP, from %s",
    157 	    ipdatalen, inet_fmt(src, s1));
    158 	return;
    159     }
    160 
    161     log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
    162 	packet_kind(igmp->igmp_type, igmp->igmp_code),
    163 	inet_fmt(src, s1), inet_fmt(dst, s2));
    164 
    165     switch (igmp->igmp_type) {
    166 
    167 	case IGMP_HOST_MEMBERSHIP_QUERY:
    168 	    accept_membership_query(src, dst, group, igmp->igmp_code);
    169 	    return;
    170 
    171 	case IGMP_v1_HOST_MEMBERSHIP_REPORT:
    172 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:
    173 	    accept_group_report(src, dst, group, igmp->igmp_type);
    174 	    return;
    175 
    176 	case IGMP_HOST_LEAVE_MESSAGE:
    177 	    accept_leave_message(src, dst, group);
    178 	    return;
    179 
    180 	case IGMP_DVMRP:
    181 	    group = ntohl(group);
    182 
    183 	    switch (igmp->igmp_code) {
    184 		case DVMRP_PROBE:
    185 		    accept_probe(src, dst,
    186 				 (char *)(igmp+1), igmpdatalen, group);
    187 		    return;
    188 
    189 		case DVMRP_REPORT:
    190  		    accept_report(src, dst,
    191 				  (char *)(igmp+1), igmpdatalen, group);
    192 		    return;
    193 
    194 		case DVMRP_ASK_NEIGHBORS:
    195 		    accept_neighbor_request(src, dst);
    196 		    return;
    197 
    198 		case DVMRP_ASK_NEIGHBORS2:
    199 		    accept_neighbor_request2(src, dst);
    200 		    return;
    201 
    202 		case DVMRP_NEIGHBORS:
    203 		    accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen,
    204 					     group);
    205 		    return;
    206 
    207 		case DVMRP_NEIGHBORS2:
    208 		    accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen,
    209 					     group);
    210 		    return;
    211 
    212 		case DVMRP_PRUNE:
    213 		    accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
    214 		    return;
    215 
    216 		case DVMRP_GRAFT:
    217 		    accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
    218 		    return;
    219 
    220 		case DVMRP_GRAFT_ACK:
    221 		    accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
    222 		    return;
    223 
    224 		default:
    225 		    log(LOG_INFO, 0,
    226 		     "ignoring unknown DVMRP message code %u from %s to %s",
    227 		     igmp->igmp_code, inet_fmt(src, s1),
    228 		     inet_fmt(dst, s2));
    229 		    return;
    230 	    }
    231 
    232  	case IGMP_PIM:
    233  	    return;
    234 
    235 	case IGMP_MTRACE_REPLY:
    236 	    return;
    237 
    238 	case IGMP_MTRACE_QUERY:
    239 	    accept_mtrace(src, dst, group, (char *)(igmp+1),
    240 		   igmp->igmp_code, igmpdatalen);
    241 	    return;
    242 
    243 	default:
    244 	    log(LOG_INFO, 0,
    245 		"ignoring unknown IGMP message type %x from %s to %s",
    246 		igmp->igmp_type, inet_fmt(src, s1),
    247 		inet_fmt(dst, s2));
    248 	    return;
    249     }
    250 }
    251 
    252 
    253 /*
    254  * Construct an IGMP message in the output packet buffer.  The caller may
    255  * have already placed data in that buffer, of length 'datalen'.  Then send
    256  * the message from the interface with IP address 'src' to destination 'dst'.
    257  */
    258 void
    259 send_igmp(src, dst, type, code, group, datalen)
    260     u_int32_t src, dst;
    261     int type, code;
    262     u_int32_t group;
    263     int datalen;
    264 {
    265     static struct sockaddr_in sdst;
    266     struct ip *ip;
    267     struct igmp *igmp;
    268 
    269     ip                      = (struct ip *)send_buf;
    270     ip->ip_src.s_addr       = src;
    271     ip->ip_dst.s_addr       = dst;
    272     ip->ip_len              = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
    273 
    274     igmp                    = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
    275     igmp->igmp_type         = type;
    276     igmp->igmp_code         = code;
    277     igmp->igmp_group.s_addr = group;
    278     igmp->igmp_cksum        = 0;
    279     igmp->igmp_cksum        = inet_cksum((u_short *)igmp,
    280 					 IGMP_MINLEN + datalen);
    281 
    282     if (IN_MULTICAST(ntohl(dst))) k_set_if(src);
    283     if (dst == allhosts_group) k_set_loop(TRUE);
    284 
    285     bzero(&sdst, sizeof(sdst));
    286     sdst.sin_family = AF_INET;
    287 #if (defined(BSD) && (BSD >= 199103))
    288     sdst.sin_len = sizeof(sdst);
    289 #endif
    290     sdst.sin_addr.s_addr = dst;
    291     if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
    292 			(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
    293 	if (errno == ENETDOWN)
    294 	    check_vif_state();
    295 	else
    296 	    log(LOG_WARNING, errno,
    297 		"sendto to %s on %s",
    298 		inet_fmt(dst, s1), inet_fmt(src, s2));
    299     }
    300 
    301     if (dst == allhosts_group) k_set_loop(FALSE);
    302 
    303     log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
    304 	packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2));
    305 }
    306