diff options
| -rw-r--r-- | common/socket.c | 107 |
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 | ||
| 318 | static 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 | |||
| 347 | static 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 | |||
| 313 | int socket_connect_addr(struct sockaddr* addr, uint16_t port) | 408 | int 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 |
