igmpproxy: Send IGMP packets with IP Router Alert option [RFC 2113] included in IP...
[tomato.git] / release / src / router / igmpproxy / src / igmp.c
blobf52572ea0352a1ebedce74e68e9b1de46f09808c
1 /*
2 ** igmpproxy - IGMP proxy based multicast router
3 ** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 **----------------------------------------------------------------------------
21 ** This software is derived work from the following software. The original
22 ** source code has been modified from it's original state by the author
23 ** of igmpproxy.
25 ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
26 ** - Licensed under the GNU General Public License, version 2
27 **
28 ** mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of
29 ** Leland Stanford Junior University.
30 ** - Original license can be found in the Stanford.txt file.
33 /**
34 * igmp.h - Recieves IGMP requests, and handle them
35 * appropriately...
38 #include "igmpproxy.h"
40 // Globals
41 uint32_t allhosts_group; /* All hosts addr in net order */
42 uint32_t allrouters_group; /* All hosts addr in net order */
44 extern int MRouterFD;
47 * Open and initialize the igmp socket, and fill in the non-changing
48 * IP header fields in the output packet buffer.
50 void initIgmp() {
51 struct ip *ip;
53 recv_buf = malloc(RECV_BUF_SIZE);
54 send_buf = malloc(RECV_BUF_SIZE);
56 k_hdr_include(true); /* include IP header when sending */
57 k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
58 k_set_ttl(1); /* restrict multicasts to one hop */
59 k_set_loop(false); /* disable multicast loopback */
61 ip = (struct ip *)send_buf;
62 memset(ip, 0, sizeof(struct ip));
64 * Fields zeroed that aren't filled in later:
65 * - IP ID (let the kernel fill it in)
66 * - Offset (we don't send fragments)
67 * - Checksum (let the kernel fill it in)
69 ip->ip_v = IPVERSION;
70 ip->ip_hl = (sizeof(struct ip) + 4) >> 2; /* +4 for Router Alert option */
71 ip->ip_tos = 0xc0; /* Internet Control */
72 ip->ip_ttl = MAXTTL; /* applies to unicasts only */
73 ip->ip_p = IPPROTO_IGMP;
75 allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
76 allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
79 /**
80 * Finds the textual name of the supplied IGMP request.
82 char *igmpPacketKind(u_int type, u_int code) {
83 static char unknown[20];
85 switch (type) {
86 case IGMP_MEMBERSHIP_QUERY: return "Membership query ";
87 case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report ";
88 case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report ";
89 case IGMP_V2_LEAVE_GROUP: return "Leave message ";
91 default:
92 sprintf(unknown, "unk: 0x%02x/0x%02x ", type, code);
93 return unknown;
98 /**
99 * Process a newly received IGMP packet that is sitting in the input
100 * packet buffer.
102 void acceptIgmp(int recvlen) {
103 register uint32_t src, dst, group;
104 struct ip *ip;
105 struct igmp *igmp;
106 int ipdatalen, iphdrlen, igmpdatalen;
108 if (recvlen < sizeof(struct ip)) {
109 my_log(LOG_WARNING, 0,
110 "received packet too short (%u bytes) for IP header", recvlen);
111 return;
114 ip = (struct ip *)recv_buf;
115 src = ip->ip_src.s_addr;
116 dst = ip->ip_dst.s_addr;
118 /* filter local multicast 239.255.255.250 */
119 if (dst == htonl(0xEFFFFFFA))
121 my_log(LOG_NOTICE, 0, "The IGMP message was local multicast. Ignoring.");
122 return;
126 * this is most likely a message from the kernel indicating that
127 * a new src grp pair message has arrived and so, it would be
128 * necessary to install a route into the kernel for this.
130 if (ip->ip_p == 0) {
131 if (src == 0 || dst == 0) {
132 my_log(LOG_WARNING, 0, "kernel request not accurate");
134 else {
135 struct IfDesc *checkVIF;
137 // Check if the source address matches a valid address on upstream vif.
138 checkVIF = getIfByIx( upStreamVif );
139 if(checkVIF == 0) {
140 my_log(LOG_ERR, 0, "Upstream VIF was null.");
141 return;
143 else if(src == checkVIF->InAdr.s_addr) {
144 my_log(LOG_NOTICE, 0, "Route activation request from %s for %s is from myself. Ignoring.",
145 inetFmt(src, s1), inetFmt(dst, s2));
146 return;
148 else if(!isAdressValidForIf(checkVIF, src)) {
149 my_log(LOG_WARNING, 0, "The source address %s for group %s, is not in any valid net for upstream VIF.",
150 inetFmt(src, s1), inetFmt(dst, s2));
151 return;
154 // Activate the route.
155 my_log(LOG_DEBUG, 0, "Route activate request from %s to %s",
156 inetFmt(src,s1), inetFmt(dst,s2));
157 activateRoute(dst, src);
161 return;
164 iphdrlen = ip->ip_hl << 2;
165 ipdatalen = ip_data_len(ip);
167 if (iphdrlen + ipdatalen != recvlen) {
168 my_log(LOG_WARNING, 0,
169 "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
170 inetFmt(src, s1), recvlen, iphdrlen, ipdatalen);
171 return;
174 igmp = (struct igmp *)(recv_buf + iphdrlen);
175 group = igmp->igmp_group.s_addr;
176 igmpdatalen = ipdatalen - IGMP_MINLEN;
177 if (igmpdatalen < 0) {
178 my_log(LOG_WARNING, 0,
179 "received IP data field too short (%u bytes) for IGMP, from %s",
180 ipdatalen, inetFmt(src, s1));
181 return;
184 my_log(LOG_NOTICE, 0, "RECV %s from %-15s to %s",
185 igmpPacketKind(igmp->igmp_type, igmp->igmp_code),
186 inetFmt(src, s1), inetFmt(dst, s2) );
188 switch (igmp->igmp_type) {
189 case IGMP_V1_MEMBERSHIP_REPORT:
190 case IGMP_V2_MEMBERSHIP_REPORT:
191 acceptGroupReport(src, group, igmp->igmp_type);
192 return;
194 case IGMP_V2_LEAVE_GROUP:
195 acceptLeaveMessage(src, group);
196 return;
198 case IGMP_MEMBERSHIP_QUERY:
199 return;
201 default:
202 my_log(LOG_INFO, 0,
203 "ignoring unknown IGMP message type %x from %s to %s",
204 igmp->igmp_type, inetFmt(src, s1),
205 inetFmt(dst, s2));
206 return;
212 * Construct an IGMP message in the output packet buffer. The caller may
213 * have already placed data in that buffer, of length 'datalen'.
215 void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) {
216 struct ip *ip;
217 struct igmp *igmp;
218 extern int curttl;
220 ip = (struct ip *)send_buf;
221 ip->ip_src.s_addr = src;
222 ip->ip_dst.s_addr = dst;
223 ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen);
225 if (IN_MULTICAST(ntohl(dst))) {
226 ip->ip_ttl = curttl;
227 } else {
228 ip->ip_ttl = MAXTTL;
231 /* Add Router Alert option */
232 ((u_char*)send_buf+MIN_IP_HEADER_LEN)[0] = IPOPT_RA;
233 ((u_char*)send_buf+MIN_IP_HEADER_LEN)[1] = 0x04;
234 ((u_char*)send_buf+MIN_IP_HEADER_LEN)[2] = 0x00;
235 ((u_char*)send_buf+MIN_IP_HEADER_LEN)[3] = 0x00;
237 igmp = (struct igmp *)(send_buf + IP_HEADER_RAOPT_LEN);
238 igmp->igmp_type = type;
239 igmp->igmp_code = code;
240 igmp->igmp_group.s_addr = group;
241 igmp->igmp_cksum = 0;
242 igmp->igmp_cksum = inetChksum((u_short *)igmp,
243 IP_HEADER_RAOPT_LEN + datalen);
248 * Call build_igmp() to build an IGMP message in the output packet buffer.
249 * Then send the message from the interface with IP address 'src' to
250 * destination 'dst'.
252 void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) {
253 struct sockaddr_in sdst;
254 int setloop = 0, setigmpsource = 0;
256 buildIgmp(src, dst, type, code, group, datalen);
258 if (IN_MULTICAST(ntohl(dst))) {
259 k_set_if(src);
260 setigmpsource = 1;
261 if (type != IGMP_DVMRP || dst == allhosts_group) {
262 setloop = 1;
263 k_set_loop(true);
267 memset(&sdst, 0, sizeof(sdst));
268 sdst.sin_family = AF_INET;
269 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
270 sdst.sin_len = sizeof(sdst);
271 #endif
272 sdst.sin_addr.s_addr = dst;
273 if (sendto(MRouterFD, send_buf,
274 IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen, 0,
275 (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
276 if (errno == ENETDOWN)
277 my_log(LOG_ERR, errno, "Sender VIF was down.");
278 else
279 my_log(LOG_INFO, errno,
280 "sendto to %s on %s",
281 inetFmt(dst, s1), inetFmt(src, s2));
284 if(setigmpsource) {
285 if (setloop) {
286 k_set_loop(false);
288 // Restore original...
289 k_set_if(INADDR_ANY);
292 my_log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
293 igmpPacketKind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
294 inetFmt(src, s1), inetFmt(dst, s2));