Commit | Line | Data |
---|---|---|
0a4d79af JH |
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 | * $Id: igmp.c,v 1.5 1993/06/23 18:47:17 pavel Exp $ | |
11 | */ | |
12 | ||
13 | ||
14 | #include "defs.h" | |
15 | ||
16 | ||
17 | /* | |
18 | * Exported variables. | |
19 | */ | |
20 | char recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer */ | |
21 | char send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer */ | |
22 | int igmp_socket; /* socket for all network I/O */ | |
23 | u_long allhosts_group; /* allhosts addr in net order */ | |
24 | u_long dvmrp_group; /* DVMRP grp addr in net order */ | |
25 | ||
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 | if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) | |
36 | log(LOG_ERR, errno, "IGMP socket"); | |
37 | ||
38 | k_hdr_include(TRUE); /* include IP header when sending */ | |
39 | k_set_rcvbuf(48*1024); /* lots of input buffering */ | |
40 | k_set_ttl(1); /* restrict multicasts to one hop */ | |
41 | k_set_loop(FALSE); /* disable multicast loopback */ | |
42 | ||
43 | ip = (struct ip *)send_buf; | |
44 | ip->ip_tos = 0; | |
45 | ip->ip_off = 0; | |
46 | ip->ip_p = IPPROTO_IGMP; | |
47 | ip->ip_ttl = MAXTTL; /* applies to unicasts only */ | |
48 | ||
49 | allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); | |
50 | dvmrp_group = htonl(INADDR_DVMRP_GROUP); | |
51 | } | |
52 | ||
53 | static char *packet_kind(type, code) | |
54 | u_char type, code; | |
55 | { | |
56 | switch (type) { | |
57 | case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; | |
58 | case IGMP_HOST_MEMBERSHIP_REPORT: return "membership report "; | |
59 | case IGMP_DVMRP: | |
60 | switch (code) { | |
61 | case DVMRP_PROBE: return "neighbor probe "; | |
62 | case DVMRP_REPORT: return "route report "; | |
63 | case DVMRP_ASK_NEIGHBORS: return "neighbor request "; | |
64 | case DVMRP_NEIGHBORS: return "neighbor list "; | |
65 | case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; | |
66 | case DVMRP_NEIGHBORS2: return "neighbor list 2 "; | |
67 | default: return "unknown DVMRP msg "; | |
68 | } | |
69 | default: return "unknown IGMP msg "; | |
70 | } | |
71 | } | |
72 | ||
73 | /* | |
74 | * Process a newly received IGMP packet that is sitting in the input | |
75 | * packet buffer. | |
76 | */ | |
77 | void accept_igmp(recvlen) | |
78 | int recvlen; | |
79 | { | |
80 | register vifi_t vifi; | |
81 | register u_long src, dst, group; | |
82 | struct ip *ip; | |
83 | struct igmp *igmp; | |
84 | int ipdatalen, iphdrlen, igmpdatalen; | |
85 | ||
86 | if (recvlen < sizeof(struct ip)) { | |
87 | log(LOG_WARNING, 0, | |
88 | "received packet too short (%u bytes) for IP header", recvlen); | |
89 | return; | |
90 | } | |
91 | ||
92 | ip = (struct ip *)recv_buf; | |
93 | src = ip->ip_src.s_addr; | |
94 | dst = ip->ip_dst.s_addr; | |
95 | iphdrlen = ip->ip_hl << 2; | |
96 | ipdatalen = ip->ip_len; | |
97 | if (iphdrlen + ipdatalen != recvlen) { | |
98 | log(LOG_WARNING, 0, | |
99 | "received packet shorter (%u bytes) than hdr+data length (%u+%u)", | |
100 | recvlen, iphdrlen, ipdatalen); | |
101 | return; | |
102 | } | |
103 | ||
104 | igmp = (struct igmp *)(recv_buf + iphdrlen); | |
105 | group = igmp->igmp_group.s_addr; | |
106 | igmpdatalen = ipdatalen - IGMP_MINLEN; | |
107 | if (igmpdatalen < 0) { | |
108 | log(LOG_WARNING, 0, | |
109 | "received IP data field too short (%u bytes) for IGMP, from %s", | |
110 | ipdatalen, inet_fmt(src, s1)); | |
111 | return; | |
112 | } | |
113 | ||
114 | log(LOG_DEBUG, 0, "RECV %s from %-15s to %s", | |
115 | packet_kind(igmp->igmp_type, igmp->igmp_code), | |
116 | inet_fmt(src, s1), inet_fmt(dst, s2)); | |
117 | ||
118 | switch (igmp->igmp_type) { | |
119 | ||
120 | case IGMP_HOST_MEMBERSHIP_QUERY: | |
121 | return; /* Answered automatically by the kernel. */ | |
122 | ||
123 | case IGMP_HOST_MEMBERSHIP_REPORT: | |
124 | accept_group_report(src, dst, group); | |
125 | return; | |
126 | ||
127 | case IGMP_DVMRP: | |
128 | switch (igmp->igmp_code) { | |
129 | ||
130 | case DVMRP_PROBE: | |
131 | accept_probe(src, dst); | |
132 | return; | |
133 | ||
134 | case DVMRP_REPORT: | |
135 | accept_report(src, dst, | |
136 | (char *)(igmp+1), igmpdatalen); | |
137 | return; | |
138 | ||
139 | case DVMRP_ASK_NEIGHBORS: | |
140 | accept_neighbor_request(src, dst); | |
141 | return; | |
142 | ||
143 | case DVMRP_ASK_NEIGHBORS2: | |
144 | accept_neighbor_request2(src, dst); | |
145 | return; | |
146 | ||
147 | case DVMRP_NEIGHBORS: | |
148 | accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen, | |
149 | group); | |
150 | return; | |
151 | ||
152 | case DVMRP_NEIGHBORS2: | |
153 | accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen, | |
154 | group); | |
155 | return; | |
156 | ||
157 | default: | |
158 | log(LOG_INFO, 0, | |
159 | "ignoring unknown DVMRP message code %u from %s to %s", | |
160 | igmp->igmp_code, inet_fmt(src, s1), | |
161 | inet_fmt(dst, s2)); | |
162 | return; | |
163 | } | |
164 | ||
165 | default: | |
166 | log(LOG_INFO, 0, | |
167 | "ignoring unknown IGMP message type %u from %s to %s", | |
168 | igmp->igmp_type, inet_fmt(src, s1), | |
169 | inet_fmt(dst, s2)); | |
170 | return; | |
171 | } | |
172 | } | |
173 | ||
174 | ||
175 | /* | |
176 | * Construct an IGMP message in the output packet buffer. The caller may | |
177 | * have already placed data in that buffer, of length 'datalen'. Then send | |
178 | * the message from the interface with IP address 'src' to destination 'dst'. | |
179 | */ | |
180 | void send_igmp(src, dst, type, code, group, datalen) | |
181 | u_long src, dst; | |
182 | int type, code; | |
183 | u_long group; | |
184 | int datalen; | |
185 | { | |
186 | static struct sockaddr_in sdst = {AF_INET}; | |
187 | struct ip *ip; | |
188 | struct igmp *igmp; | |
189 | ||
190 | ip = (struct ip *)send_buf; | |
191 | ip->ip_src.s_addr = src; | |
192 | ip->ip_dst.s_addr = dst; | |
193 | ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; | |
194 | ||
195 | igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); | |
196 | igmp->igmp_type = type; | |
197 | igmp->igmp_code = code; | |
198 | igmp->igmp_group.s_addr = group; | |
199 | igmp->igmp_cksum = 0; | |
200 | igmp->igmp_cksum = inet_cksum((u_short *)igmp, | |
201 | IGMP_MINLEN + datalen); | |
202 | ||
203 | if (IN_MULTICAST(ntohl(dst))) k_set_if(src); | |
204 | if (dst == allhosts_group) k_set_loop(TRUE); | |
205 | ||
206 | sdst.sin_addr.s_addr = dst; | |
207 | if (sendto(igmp_socket, send_buf, ip->ip_len, 0, | |
208 | (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { | |
209 | if (errno == ENETDOWN) check_vif_state(); | |
210 | else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1)); | |
211 | } | |
212 | ||
213 | if (dst == allhosts_group) k_set_loop(FALSE); | |
214 | ||
215 | log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", | |
216 | packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); | |
217 | } |