summaryrefslogtreecommitdiffstats
path: root/common/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/socket.c')
-rw-r--r--common/socket.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/common/socket.c b/common/socket.c
index cf6e9eb..6b4a596 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -43,6 +43,10 @@ static int wsa_init = 0;
43#include <netdb.h> 43#include <netdb.h>
44#include <arpa/inet.h> 44#include <arpa/inet.h>
45#include <fcntl.h> 45#include <fcntl.h>
46#ifdef AF_INET6
47#include <net/if.h>
48#include <ifaddrs.h>
49#endif
46#endif 50#endif
47#include "socket.h" 51#include "socket.h"
48 52
@@ -310,6 +314,97 @@ int socket_create(uint16_t port)
310 return sfd; 314 return sfd;
311} 315}
312 316
317#ifdef AF_INET6
318static uint32_t _in6_addr_scope(struct in6_addr* addr)
319{
320 uint32_t scope = 0;
321
322 if (IN6_IS_ADDR_MULTICAST(addr)) {
323 if (IN6_IS_ADDR_MC_NODELOCAL(addr)) {
324 scope = 1;
325 } else if (IN6_IS_ADDR_MC_LINKLOCAL(addr)) {
326 scope = 2;
327 } else if (IN6_IS_ADDR_MC_SITELOCAL(addr)) {
328 scope = 3;
329 }
330
331 return scope;
332 }
333
334 if (IN6_IS_ADDR_LINKLOCAL(addr)) {
335 scope = 2;
336 } else if (IN6_IS_ADDR_SITELOCAL(addr)) {
337 scope = 3;
338 } else if (IN6_IS_ADDR_LOOPBACK(addr)) {
339 scope = 4;
340 } else if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
341 scope = 0;
342 }
343
344 return scope;
345}
346
347static int32_t _in6_addr_scope_id(struct in6_addr* addr)
348{
349 int32_t res = -1;
350
351 struct ifaddrs *ifaddr, *ifa;
352 uint32_t addr_scope;
353
354 /* get scope for requested address */
355 addr_scope = _in6_addr_scope(addr);
356 if (addr_scope == 0) {
357 /* global scope doesn't need a specific scope id */
358 return addr_scope;
359 }
360
361 /* get interfaces */
362 if (getifaddrs(&ifaddr) == -1) {
363 perror("getifaddrs");
364 return res;
365 }
366
367 /* loop over interfaces */
368 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
369 /* skip if no address is available */
370 if (ifa->ifa_addr == NULL) {
371 continue;
372 }
373
374 /* skip if wrong family */
375 if (ifa->ifa_addr->sa_family != AF_INET6) {
376 continue;
377 }
378
379 /* skip if not up */
380 if ((ifa->ifa_flags & IFF_UP) == 0) {
381 continue;
382 }
383
384 /* skip if not running */
385 if ((ifa->ifa_flags & IFF_RUNNING) == 0) {
386 continue;
387 }
388
389 struct sockaddr_in6* addr_in = (struct sockaddr_in6*)ifa->ifa_addr;
390
391 /* skip if scopes do not match */
392 if (_in6_addr_scope(&addr_in->sin6_addr) != addr_scope) {
393 continue;
394 }
395
396 /* use the scope id of this interface */
397 res = addr_in->sin6_scope_id;
398
399 break;
400 }
401
402 freeifaddrs(ifaddr);
403
404 return res;
405}
406#endif
407
313int socket_connect_addr(struct sockaddr* addr, uint16_t port) 408int socket_connect_addr(struct sockaddr* addr, uint16_t port)
314{ 409{
315 int sfd = -1; 410 int sfd = -1;
@@ -337,6 +432,18 @@ int socket_connect_addr(struct sockaddr* addr, uint16_t port)
337 else if (addr->sa_family == AF_INET6) { 432 else if (addr->sa_family == AF_INET6) {
338 struct sockaddr_in6* addr_in = (struct sockaddr_in6*)addr; 433 struct sockaddr_in6* addr_in = (struct sockaddr_in6*)addr;
339 addr_in->sin6_port = htons(port); 434 addr_in->sin6_port = htons(port);
435
436 /*
437 * IPv6 Routing Magic:
438 *
439 * If the scope of the address is a link-local one, IPv6 requires the
440 * scope id set to an interface number to allow proper routing. However,
441 * as the provided sockaddr might contain a wrong scope id, we must find
442 * a scope id from a suitable interface on this system or routing might
443 * fail. An IPv6 guru should have another look though...
444 */
445 addr_in->sin6_scope_id = _in6_addr_scope_id(&addr_in->sin6_addr);
446
340 addrlen = sizeof(struct sockaddr_in6); 447 addrlen = sizeof(struct sockaddr_in6);
341 } 448 }
342#endif 449#endif