switchbrew / libnx

Library for Switch Homebrew

Home Page:https://switchbrew.github.io/libnx/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: sys/socket.h multicast capabilities

fra189 opened this issue · comments

Hi there,
I'm trying to implement mDNS protocol in my nintendo switch brew.
My approach:

#include <switch.h>
...
socketInitialize();

 sock = socket(AF_INET, SOCK_DGRAM, 0);
 setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &param, sizeof(param));
 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &enable, sizeof(enable));
 setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (void*) &param, sizeof(param));
 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,(void*) &enable, sizeof(enable));
 setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void*) &addr, sizeof(addr));
 setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mreq, sizeof(mreq));

....
socketExit();

...

Triyng to setup IP_MULTICAST_TTL and IP_ADD_MEMBERSHIP flags, I obtain an "operation not permitted" error (errno 1).
What am I missing?

Thank you in advance.

What did you fill into addr for IP_MULTICAST_IF and mreq for IP_ADD_MEMBERSHIP?

Hi,
Thank you for answering.
So this is the part of the code:

#define MDNS_MULTICAST_ADDRESS "224.0.0.251"
#define MDNS_PORT 5353

int init(struct in_addr host) {
    struct ip_mreq mreq;
    struct sockaddr_in addr;
    int sock;
    char param;
    
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0) {
       return sock;
     }

    param = 32;
    if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &param, sizeof(param)) < 0) {
      return -1;
    }
    
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(MDNS_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;
    
    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
      return -1;
    }
    
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr(MDNS_MULTICAST_ADDRESS);
    mreq.imr_interface.s_addr = host.s_addr;
  
    if (setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void*) &mreq.imr_interface.s_addr, sizeof(mreq.imr_interface.s_addr)) < 0)  {
        return -1;
    }
    if(setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mreq, sizeof(mreq)) < 0) {
       return -1;
    }
    return sock;
  }
  
  .....
  struct in_addr host;
  const long ip = gethostid();
  char ip_str[32] = {0};
  inet_ntop(AF_INET, &ip, ip_str, sizeof(ip_str));
  host.s_addr = inet_addr(ip_str);  
  int sock = init(host);

I get EPERM for every multicast-related socket option. It's possible the OS won't allow unprivileged multicast servers.

@fra189 fwiw, if you're just looking to resolve mDNS services on your network (rather than advertise them), you may be able to avoid having to join the multicast group to get a response. You can send an mDNS query and request a unicast response by setting the unicast-response bit in the header. It doesn't guarantee a unicast response, but it generally should result in one (especially if you send QU queries periodically if you get no response).

See https://datatracker.ietf.org/doc/html/rfc6762#section-5.4

Thank you @cgutman
I can make a try with that.

I figured out how to join a multicast group, just pass in higher permissions when initializing the socket.

SocketInitConfig cfg = *(socketGetDefaultInitConfig());
cfg.bsd_service_type = BsdServiceType_Auto; // or BsdServiceType_System
socketInitialize(&cfg);

This can also be used to help create an 80 port HTTP service, hoping this comment would help someone ~

I think this is actually about library compatibility version, not about permissions.

This is untested and based on RE, but I think the line that causes EPERM is this one: https://github.com/switchbrew/libnx/blob/master/nx/source/runtime/devices/socket.c#L65

If I am reading this permission check function right, the following require library version > 3:

IP_MULTICAST_IF
IP_MULTICAST_TTL
IP_MULTICAST_LOOP
IP_ADD_MEMBERSHIP
IP_DROP_MEMBERSHIP
IP_MULTICAST_VIF

I think BsdServiceType_System just causes an early return in the permission checking function, which bypasses the library compatibility check entirely.

Thanks for the detailed explanation, I confirmed that after setting cfg.bsdsockets_version = 4;, multicast is indeed working.

Fixed by libnx v4.5.0.