2 ** igmpproxy - IGMP proxy based multicast router
3 ** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
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.
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
25 ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
26 ** - Licensed under the GNU General Public License, version 2
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.
34 * igmp.h - Recieves IGMP requests, and handle them
38 #include "igmpproxy.h"
41 uint32_t allhosts_group
; /* All hosts addr in net order */
42 uint32_t allrouters_group
; /* All hosts addr in net order */
47 * Open and initialize the igmp socket, and fill in the non-changing
48 * IP header fields in the output packet buffer.
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)
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
);
80 * Finds the textual name of the supplied IGMP request.
82 char *igmpPacketKind(u_int type
, u_int code
) {
83 static char unknown
[20];
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 ";
92 sprintf(unknown
, "unk: 0x%02x/0x%02x ", type
, code
);
99 * Process a newly received IGMP packet that is sitting in the input
102 void acceptIgmp(int recvlen
) {
103 register uint32_t src
, dst
, group
;
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
);
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.");
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.
131 if (src
== 0 || dst
== 0) {
132 my_log(LOG_WARNING
, 0, "kernel request not accurate");
135 struct IfDesc
*checkVIF
;
137 // Check if the source address matches a valid address on upstream vif.
138 checkVIF
= getIfByIx( upStreamVif
);
140 my_log(LOG_ERR
, 0, "Upstream VIF was null.");
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
));
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
));
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
);
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
);
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
));
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
);
194 case IGMP_V2_LEAVE_GROUP
:
195 acceptLeaveMessage(src
, group
);
198 case IGMP_MEMBERSHIP_QUERY
:
203 "ignoring unknown IGMP message type %x from %s to %s",
204 igmp
->igmp_type
, inetFmt(src
, s1
),
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
) {
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
))) {
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
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
))) {
261 if (type
!= IGMP_DVMRP
|| dst
== allhosts_group
) {
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
);
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.");
279 my_log(LOG_INFO
, errno
,
280 "sendto to %s on %s",
281 inetFmt(dst
, s1
), inetFmt(src
, s2
));
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
));