1 /* $Id: ipx_mcast4.c,v 1.3 2003-11-26 12:26:25 btb Exp $ */
5 * "ipx driver" for IPv4 multicasting
26 #include "ipx_mcast4.h"
29 #include "../../main/multi.h"
30 #include "../../main/newmenu.h"
32 //#define IPX_MCAST4_DEBUG
34 extern unsigned char ipx_MyAddress[10];
36 #define UDP_BASEPORT 28342
37 #define PORTSHIFT_TOLERANCE 0x100
38 #define MAX_PACKETSIZE 8192
40 /* OUR port. Can be changed by "@X[+=]..." argument (X is the shift value)
42 static int baseport=UDP_BASEPORT;
44 static struct in_addr game_addr; // The game's multicast address
46 #define MSGHDR "IPX_mcast4: "
49 #ifdef IPX_MCAST4_DEBUG
50 static void msg(const char *fmt, ...)
54 fputs(MSGHDR, stdout);
64 # ifdef IPX_MCAST4_DEBUG
72 #define FAIL(m...) do{ nm_messagebox("Error", 1, "Ok", ##m); return -1; } while (0)
74 # define FAIL() do{ nm_messagebox("Error", 1, "Ok", "IPX_mcast4 failure"); return -1; } while (0)
77 #ifdef IPX_MCAST4_DEBUG
78 /* Dump raw form of IP address/port by fancy output to user
80 static void dumpraddr(unsigned char *a)
84 printf("[%u.%u.%u.%u]", a[0], a[1], a[2], a[3]);
85 port=(signed short)ntohs(*(unsigned short *)(a+4));
86 if (port) printf(":%+d",port);
89 /* Like dumpraddr() but for structure "sockaddr_in"
91 static void dumpaddr(struct sockaddr_in *sin)
94 unsigned char qhbuf[8];
96 memcpy(qhbuf + 0, &sin->sin_addr, 4);
97 ports = htons(((short)ntohs(sin->sin_port)) - UDP_BASEPORT);
98 memcpy(qhbuf + 4, &ports, 2);
103 // The multicast address for Descent 2 game announcements.
104 // TODO: Pick a better address for this
105 #define DESCENT2_ANNOUNCE_ADDR inet_addr("239.255.1.2")
107 /* Open the socket and subscribe to the multicast session */
108 static int ipx_mcast4_OpenSocket(ipx_socket_t *sk, int port)
112 struct sockaddr_in sin;
115 if((sk->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
118 FAIL("socket() creation failed on port %d: %m", port);
122 sin.sin_family = AF_INET;
123 sin.sin_addr.s_addr = htonl(INADDR_ANY);
124 sin.sin_port = htons(baseport);
125 if (bind(sk->fd, (struct sockaddr *)&sin, sizeof(sin)))
127 if (closesocket(sk->fd))
128 msg("close() failed during error recovery: %m");
130 FAIL("bind() to UDP port %d failed: %m", baseport);
133 // Set the TTL so the packets can get out of the local network.
134 if(setsockopt(sk->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const void*)&ttl, sizeof(ttl)) < 0)
135 FAIL("setsockopt() failed to set TTL to 128");
137 // Disable multicast loopback
139 if(setsockopt(sk->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void*)&loop, sizeof(loop)) < 0)
140 FAIL("setsockopt() failed to disable multicast loopback: %m");
142 // Subscribe to the game announcement address
143 memset(&mreq, 0, sizeof(mreq));
144 mreq.imr_multiaddr.s_addr = DESCENT2_ANNOUNCE_ADDR;
145 mreq.imr_interface.s_addr = INADDR_ANY;
146 if(setsockopt(sk->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void*)&mreq, sizeof(mreq)) < 0)
147 FAIL("setsockopt() failed to subscribe to the game announcement multicast group");
149 // We're not subscribed to a game address yet
150 game_addr.s_addr = 0;
156 static void ipx_mcast4_CloseSocket(ipx_socket_t *sk)
158 if(closesocket(sk->fd) < 0)
163 static int ipx_mcast4_SendPacket(ipx_socket_t *sk, IPXPacket_t *IPXHeader, u_char *data, int dataLen)
165 struct sockaddr_in toaddr;
168 msg("SendPacket enter, dataLen=%d", dataLen);
170 if(dataLen < 0 || dataLen > MAX_PACKETSIZE)
173 toaddr.sin_family = AF_INET;
174 memcpy(&toaddr.sin_addr, IPXHeader->Destination.Node + 0, 4);
175 //toaddr.sin_port = htons(((short)ntohs(*(unsigned short *)(IPXHeader->Destination.Node + 4))) + UDP_BASEPORT);
176 // For now, just use the same port for everything
177 toaddr.sin_port = htons(UDP_BASEPORT);
179 // If it's the broadcast address, then we want to send it to the
180 // GAME ANNOUNCEMENT address.
181 // Data to be sent to the GAME has the destination already set by
182 // ipx_mcast4_SendGamePacket
183 if(toaddr.sin_addr.s_addr == INADDR_BROADCAST)
184 toaddr.sin_addr.s_addr = DESCENT2_ANNOUNCE_ADDR;
186 #ifdef IPX_MCAST4_DEBUG
187 printf(MSGHDR "sendto((%d),Node=[4] %02X %02X,Socket=%02X %02X,s_port=%u,",
189 IPXHeader->Destination.Node[4], IPXHeader->Destination.Node[5],
190 IPXHeader->Destination.Socket[0], IPXHeader->Destination.Socket[1],
191 ntohs(toaddr.sin_port));
196 i = sendto(sk->fd, data, dataLen, 0, (struct sockaddr *)&toaddr, sizeof(toaddr));
200 static int ipx_mcast4_ReceivePacket(ipx_socket_t *sk, char *outbuf, int outbufsize, struct ipx_recv_data *rd)
203 struct sockaddr_in fromaddr;
204 int fromaddrsize = sizeof(fromaddr);
206 if((size = recvfrom(sk->fd, outbuf, outbufsize, 0, (struct sockaddr*)&fromaddr, &fromaddrsize)) < 0)
209 #ifdef IPX_MCAST4_DEBUG
210 printf(MSGHDR "Got packet from ");
215 // We have the packet, now fill out the receive data.
216 memset(rd, 0, sizeof(*rd));
217 memcpy(rd->src_node, &fromaddr.sin_addr, 4);
218 // TODO: Include the port like in ipx_udp.c
224 /* Handle the netgame aux data
225 * Byte 0 is the protocol version number.
226 * Bytes 1-4 are the IPv4 multicast session to join, in network byte order.
228 static int ipx_mcast4_HandleNetgameAuxData(ipx_socket_t *sk, const u_char buf[NETGAME_AUX_SIZE])
230 // Extract the multicast session and subscribe to it. We should
231 // now be getting packets intended for the players of this game.
233 // Note that we stay subscribed to the game announcement session,
234 // so we can reply to game info requests
238 // Check the protocol version
239 if(buf[0] != IPX_MCAST4_VERSION)
241 FAIL("mcast4 protocol\nversion mismatch!\nGame version is %02x,\nour version is %02x", buf[0], IPX_MCAST4_VERSION);
244 // Get the multicast session
245 memcpy(&game_addr, buf + 1, sizeof(game_addr));
247 #ifdef IPX_MCAST4_DEBUG
249 struct sockaddr_in tmpaddr;
250 tmpaddr.sin_addr = game_addr;
251 tmpaddr.sin_port = 0;
253 printf("Handling netgame aux data: Subscribing to ");
259 // Set the TTL so the packets can get out of the local network.
260 if(setsockopt(sk->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const void*)&ttl, sizeof(ttl)) < 0)
261 FAIL("setsockopt() failed to set TTL to 128");
263 memset(&mreq, 0, sizeof(mreq));
264 mreq.imr_multiaddr = game_addr;
265 mreq.imr_interface.s_addr = INADDR_ANY;
267 if(setsockopt(sk->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void*)&mreq, sizeof(mreq)) < 0)
268 FAIL("setsockopt() failed to subscribe to the game group");
273 /* Create the netgame aux data.
274 * Byte 0 is the protcol version number.
275 * Bytes 1-4 hold the IPv4 multicast session for the game.
277 static void ipx_mcast4_InitNetgameAuxData(ipx_socket_t *sk, u_char buf[NETGAME_AUX_SIZE])
280 Assert(game_addr.s_addr == 0);
282 // The first byte is the version number
283 buf[0] = IPX_MCAST4_VERSION;
285 // Generate a random session
286 // game_addr = inet_makeaddr(239*256 + 255, d_rand() % 0xFFFF);
287 sprintf(addr, "%i.%i.%i.%i", 239, 255, d_rand() % 0xFF, d_rand() % 0xFF);
288 game_addr.s_addr = inet_addr(addr);
289 memcpy(buf + 1, &game_addr, sizeof(game_addr));
291 // Since we're obviously the hosting machine, subscribe to this address
292 ipx_mcast4_HandleNetgameAuxData(sk, buf);
295 static void ipx_mcast4_HandleLeaveGame(ipx_socket_t *sk)
297 // We left the game, so unsubscribe from its multicast session
300 Assert(game_addr.s_addr != 0);
302 #ifdef IPX_MCAST4_DEBUG
303 printf("Unsubscribing from game's multicast group: ");
304 dumpraddr(&game_addr.s_addr);
308 mreq.imr_multiaddr = game_addr;
309 mreq.imr_interface.s_addr = INADDR_ANY;
310 if(setsockopt(sk->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void*)&mreq, sizeof(mreq)) < 0)
311 msg("setsockopt() failed unsubscribing from previous group!");
312 game_addr.s_addr = 0;
315 // Send a packet to every member of the game. We can just multicast it here.
316 static int ipx_mcast4_SendGamePacket(ipx_socket_t *sk, ubyte *data, int dataLen)
318 struct sockaddr_in toaddr;
321 memset(&toaddr, 0, sizeof(toaddr));
322 toaddr.sin_family = AF_INET;
323 toaddr.sin_addr = game_addr;
324 toaddr.sin_port = htons(UDP_BASEPORT);
326 msg("ipx_mcast4_SendGamePacket");
328 i = sendto(sk->fd, data, dataLen, 0, (struct sockaddr *)&toaddr, sizeof(toaddr));
333 // Pull this in from ipx_udp.c since it's the same for us.
334 extern int ipx_udp_GetMyAddress();
336 struct ipx_driver ipx_mcast4 = {
337 ipx_udp_GetMyAddress,
338 ipx_mcast4_OpenSocket,
339 ipx_mcast4_CloseSocket,
340 ipx_mcast4_SendPacket,
341 ipx_mcast4_ReceivePacket,
342 ipx_general_PacketReady,
343 ipx_mcast4_InitNetgameAuxData,
344 ipx_mcast4_HandleNetgameAuxData,
345 ipx_mcast4_HandleLeaveGame,
346 ipx_mcast4_SendGamePacket