2016-02-01 24 views
5

Ho bisogno di ricreare un servizio su linux che era solito eseguire su un sistema embedded che eseguiva lo stack LwIP (IP leggero).Come creare un servizio che invia/riceve trasmissioni UDP su più interfacce

Il servizio utilizza trasmissioni UDP su INADDR_BROADCAST (255.255.255.255) per trovare e configurare i dispositivi sulla stessa subnet fisica. Invia una "scansione" e tutti i dispositivi che eseguono questo servizio rispondono con la loro configurazione di rete completa (tutte le NIC, tutti i loro MAC e IP). Quindi un utente ottiene un elenco di tali dispositivi e può modificare l'impostazione IP (utilizzando il protocollo già esistente).
[Sì, so che le persone usano DHCP per questo, ma stiamo parlando del settore industriale qui e il protocollo/servizio esiste già, quindi non ho altra scelta che implementare qualcosa compatibile]

Poiché il dispositivo ha diversi NIC, devo essere in grado di ricevere questa trasmissione, sapere quale NIC lo ha ricevuto e inviare una risposta tramite quella NIC. Anche il servizio è configurabile in modo che non apra un socket su NIC specifiche.

Lo stack LwIP non è sofisticato come lo stack Linux, quindi un socket associato a un IP continua a ricevere tutti i pacchetti su INADDR_BROADCAST. Quindi è stato abbastanza semplice implementarlo.

Su Linux, ho pensato ho diverse opzioni per farlo:

  • aperte prese individuali per ogni scheda di rete con SO_BROADCAST e SO_BINDTODEVICE, in modo che io possa bind() a INADDR_ANY e ricevere le trasmissioni. Quando invio una risposta tramite quel socket, il routing di Linux viene ignorato e viene inviato tramite la NIC desiderata.
    MA: Vorrei il servizio di non correre come root ...
  • avere un unico INADDR_ANY socket associato (possibilmente con IP_PKTINFO sapere facilmente quale NIC il pacchetto è arrivato via), hanno una presa per ogni scheda di rete, legato ad un indirizzo valido, con SO_BROADCAST e inviare risposte tramite quelle. Se vado in questo modo mi piacerebbe assicurarmi che le prese di invio non possano mai ricevere nulla (perché non ho mai chiamato recv() su di esse. Resource starvation?).
    Forse SO_RCVBUFSIZE = 0 sarebbe sufficiente?

Quale sarebbe il modo corretto di implementarlo?

+0

Molto difficile rispondere semplicemente senza un'analisi adeguata. Prova a creare un semplice mokeup per completare la tua analisi. (Non so in quale settore industriale funzioni questo servizio, ma ho paura della sicurezza ...) – rom1nux

+0

il protocollo è concepito per l'installazione iniziale e non consente di modificare l'impostazione IP quando il sistema è in modalità "Esegui". – TabascoEye

+0

Penso che un minimo silenzioso rende 'SO_RCVBUFSIZE = 0' non utile. Puoi usare 'shutdown (sockfd, SHUT_RD)' per disabilitare ulteriori letture, ma sarei incline a scrivere correttamente il programma :) –

risposta

2

È possibile installare il binario con CAP_NET_RAW (e CAP_NET_BIND_SERVICE se vengono utilizzate le porte ≤ 1024); setcap 'cap_net_raw=ep' yourdaemon come root. Per IP, SO_BROADCAST non richiede alcuna funzionalità (in particolare, CAP_NET_BROADCAST non viene utilizzato per IP).

(per le capacità esatti necessari, si veda ad esempio net/core/sock.c:sock_setbindtodevice(), net/core/sock.c:sock_setsockopt(), e include/net/sock.h:sock_set_flag() nei sorgenti del kernel Linux per la verifica.)

Tuttavia, i demoni sono in genere iniziato come root. Qui, quanto sopra non sarebbe sufficiente, come la modifica dell'ID utente per il processo (per eliminare i privilegi) anche clears the effective capabilities. Tuttavia, anch'io preferisco i miei servizi per funzionare con privilegi limitati.

vorrei scegliere tra due approcci di base:

  1. richiedono che il demone viene eseguito da root, o con CAP_NET_RAW (ed eventualmente CAP_NET_BIND_SERVICE) capacità.

    Uso prctl(), setgroups() o initgroups(), setresuid(), setresgid(), e capacità da libcap, cap_init(), cap_set_flag() e cap_set_proc() rimozione dei privilegi passando a un utente dedicato e gruppo, ma mantenendo il CAP_NET_RAW (e facoltativamente CAP_NET_BIND_SERVICE) e loro solo .

    Ciò consente al daemon di rispondere ad es. Segnale HUP senza riavvio completo, poiché dispone dei privilegi necessari per enumerare le interfacce e leggere i propri file di configurazione per aprire i socket per le nuove interfacce.

  2. Utilizzare un "loader" con privilegi, che apre tutti i socket necessari, elimina i privilegi ed esegue il daemon effettivo.

    Il demone dovrebbe ottenere i dettagli di socket e interfaccia come parametri della riga di comando, o forse tramite input standard. Il demone è completamente privo di privilegi.

    Sfortunatamente, se vengono aperte nuove interfacce o la configurazione viene modificata, il daemon non può fare molto eccetto uscire. (Non può nemmeno eseguire il caricatore privilegiata, perché i privilegi sono già stati eliminati.)

Il primo approccio è più comune, e più facile da implementare nella pratica; specialmente se il demone dovrebbe essere eseguito solo da root. (Ricordate, il demone può rispondere alle modifiche di configurazione, poiché ha le capacità necessarie ma non i privilegi di root in generale.) Ho usato solo il secondo approccio per i binari "black box" di cui non mi fido.


Ecco alcuni esempi di codice.

privileges.h: #ifndef PRIVILEGES_H #define PRIVILEGES_H

#define NEED_CAP_NET_ADMIN   (1U << 0) 
#define NEED_CAP_NET_BIND_SERVICE (1U << 1) 
#define NEED_CAP_NET_RAW   (1U << 2) 

extern int drop_privileges(const char *const user, const unsigned int capabilities); 

#endif /* PRIVILEGES_H */ 

privileges.c:

#define _GNU_SOURCE 
#define _BSD_SOURCE 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/capability.h> 
#include <sys/prctl.h> 
#include <errno.h> 
#include <pwd.h> 
#include <grp.h> 
#include "privileges.h" 

/* Only three NEED_CAP_ constants defined. */ 
#define MAX_CAPABILITIES 3 

static int permit_effective(cap_t caps, const unsigned int capabilities) 
{ 
    cap_value_t value[MAX_CAPABILITIES]; 
    int   values = 0; 

    if (capabilities & NEED_CAP_NET_ADMIN) 
     value[values++] = CAP_NET_ADMIN; 

    if (capabilities & NEED_CAP_NET_BIND_SERVICE) 
     value[values++] = CAP_NET_BIND_SERVICE; 

    if (capabilities & NEED_CAP_NET_RAW) 
     value[values++] = CAP_NET_RAW; 

    if (values < 1) 
     return 0; 

    if (cap_set_flag(caps, CAP_PERMITTED, values, value, CAP_SET) == -1) 
     return errno; 
    if (cap_set_flag(caps, CAP_EFFECTIVE, values, value, CAP_SET) == -1) 
     return errno; 

    return 0; 
} 

static int add_privileges(cap_t caps) 
{ 
    cap_value_t value[3] = { CAP_SETPCAP, CAP_SETUID, CAP_SETGID }; 

    if (cap_set_flag(caps, CAP_PERMITTED, sizeof value/sizeof value[0], value, CAP_SET) == -1) 
     return errno; 

    if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof value/sizeof value[0], value, CAP_SET) == -1) 
     return errno; 

    return 0; 
} 

int drop_privileges(const char *const user, const unsigned int capabilities) 
{ 
    uid_t uid; 
    gid_t gid; 
    cap_t caps; 

    /* Make sure user is neither NULL nor empty. */ 
    if (!user || !user[0]) 
     return errno = EINVAL; 

    /* Find the user. */ 
    { 
     struct passwd *pw; 

     pw = getpwnam(user); 
     if (!pw 
#ifdef UID_MIN 
      || pw->pw_uid < (uid_t)UID_MIN 
#endif 
#ifdef UID_MAX 
      || pw->pw_uid > (uid_t)UID_MAX 
#endif 
#ifdef GID_MIN 
      || pw->pw_gid < (gid_t)GID_MIN 
#endif 
#ifdef GID_MAX 
      || pw->pw_gid > (gid_t)GID_MAX 
#endif 
       ) 
      return errno = EINVAL; 

     uid = pw->pw_uid; 
     gid = pw->pw_gid; 

     endpwent(); 
    } 

    /* Install privileged capabilities. */ 
    caps = cap_init(); 
    if (!caps) 
     return errno = ENOMEM; 
    if (permit_effective(caps, capabilities)) { 
     const int cause = errno; 
     cap_free(caps); 
     return errno = cause; 
    } 
    if (add_privileges(caps)) { 
     const int cause = errno; 
     cap_free(caps); 
     return errno = cause; 
    } 
    if (cap_set_proc(caps) == -1) { 
     const int cause = errno; 
     cap_free(caps); 
     return errno = cause; 
    } 
    cap_free(caps); 

    /* Retain permitted capabilities over the identity change. */ 
    prctl(PR_SET_KEEPCAPS, 1UL, 0UL,0UL,0UL); 

    if (setresgid(gid, gid, gid) == -1) 
     return errno = EPERM; 

    if (initgroups(user, gid) == -1) 
     return errno = EPERM; 

    if (setresuid(uid, uid, uid) == -1) 
     return errno = EPERM; 

    /* Install unprivileged capabilities. */ 
    caps = cap_init(); 
    if (!caps) 
     return errno = ENOMEM; 
    if (permit_effective(caps, capabilities)) { 
     const int cause = errno; 
     cap_free(caps); 
     return errno = cause; 
    } 
    if (cap_set_proc(caps) == -1) { 
     const int cause = errno; 
     cap_free(caps); 
     return errno = cause; 
    } 
    cap_free(caps); 

    /* Reset standard KEEPCAPS behaviour. */ 
    prctl(PR_SET_KEEPCAPS, 0UL, 0UL,0UL,0UL); 

    /* Done. */ 
    return 0; 
} 

udp-broadcast.h:

#ifndef UDP_BROADCAST_H 
#define UDP_BROADCAST_H 
#include <stdlib.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

struct udp_socket { 
    struct sockaddr_in broadcast; /* Broadcast address */ 
    unsigned int  if_index; /* Interface index */ 
    int     descriptor; /* Socket descriptor */ 
}; 

extern int open_udp_broadcast(struct udp_socket *const udpsocket, 
           const char  *const interface, 
           int    const port); 

extern int udp_broadcast(const struct udp_socket *const udpsocket, 
         const void *const    data, 
         const size_t     size, 
         const int      flags); 

extern size_t udp_receive(const struct udp_socket *const udpsocket, 
          void *const     data, 
          const size_t     size_max, 
          const int      flags, 
          struct sockaddr_in  *const from_addr, 
          struct sockaddr_in  *const to_addr, 
          struct sockaddr_in  *const hdr_addr, 
          unsigned int   *const if_index); 

#endif /* UDP_BROADCAST_H */ 

udp-broadcast.c:

#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <net/if.h> 
#include <errno.h> 
#include "udp-broadcast.h" 


int udp_broadcast(const struct udp_socket *const udpsocket, 
        const void *const    data, 
        const size_t     size, 
        const int      flags) 
{ 
    ssize_t n; 

    if (!udpsocket || udpsocket->broadcast.sin_family != AF_INET) 
     return errno = EINVAL; 

    if (!data || size < 1) 
     return 0; 

    n = sendto(udpsocket->descriptor, data, size, flags, 
       (const struct sockaddr *)&(udpsocket->broadcast), 
       sizeof (struct sockaddr_in)); 

    if (n == (ssize_t)-1) 
     return errno; 
    if (n == (ssize_t)size) 
     return 0; 
    return errno = EIO;  
} 


size_t udp_receive(const struct udp_socket *const udpsocket, 
        void *const     data, 
        const size_t     size_max, 
        const int      flags, 
        struct sockaddr_in  *const from_addr, 
        struct sockaddr_in  *const to_addr, 
        struct sockaddr_in  *const hdr_addr, 
        unsigned int   *const if_index) 
{ 
    char   ancillary[512]; 
    struct msghdr msg; 
    struct iovec iov[1]; 
    struct cmsghdr *cmsg; 
    ssize_t   n; 

    if (!data || size_max < 1 || !udpsocket) { 
     errno = EINVAL; 
     return (size_t)0; 
    } 

    /* Clear results, just in case. */ 
    if (from_addr) { 
     memset(from_addr, 0, sizeof *from_addr); 
     from_addr->sin_family = AF_UNSPEC; 
    } 
    if (to_addr) { 
     memset(to_addr, 0, sizeof *to_addr); 
     to_addr->sin_family = AF_UNSPEC; 
    } 
    if (hdr_addr) { 
     memset(hdr_addr, 0, sizeof *hdr_addr); 
     hdr_addr->sin_family = AF_UNSPEC; 
    } 
    if (if_index) 
     *if_index = 0U; 

    iov[0].iov_base = data; 
    iov[0].iov_len = size_max; 

    if (from_addr) { 
     msg.msg_name = from_addr; 
     msg.msg_namelen = sizeof (struct sockaddr_in); 
    } else { 
     msg.msg_name = NULL; 
     msg.msg_namelen = 0; 
    } 

    msg.msg_iov = iov; 
    msg.msg_iovlen = 1; 

    msg.msg_control = ancillary; 
    msg.msg_controllen = sizeof ancillary; 

    msg.msg_flags = 0; 

    n = recvmsg(udpsocket->descriptor, &msg, flags); 
    if (n == (ssize_t)-1) 
     return (size_t)0; /* errno set by recvmsg(). */ 
    if (n < (ssize_t)1) { 
     errno = EIO; 
     return (size_t)0; 
    } 

    /* Populate data from ancillary message, if requested. */ 
    if (to_addr || hdr_addr || if_index) 
     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) 
      if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { 
       const struct in_pktinfo *const info = CMSG_DATA(cmsg); 
       if (!info) 
        continue; 
       if (if_index) 
        *if_index = info->ipi_ifindex; 
       if (to_addr) { 
        to_addr->sin_family = AF_INET; 
        to_addr->sin_port = udpsocket->broadcast.sin_port; /* This is a guess. */ 
        to_addr->sin_addr = info->ipi_spec_dst; 
       } 
       if (hdr_addr) { 
        hdr_addr->sin_family = AF_INET; 
        hdr_addr->sin_port = udpsocket->broadcast.sin_port; /* A guess, again. */ 
        hdr_addr->sin_addr = info->ipi_addr; 
       } 
      } 

    errno = 0; 
    return (size_t)n; 
} 

int open_udp_broadcast(struct udp_socket *const udpsocket, 
         const char  *const interface, 
         int    const port) 
{ 
    const size_t interface_len = (interface) ? strlen(interface) : 0; 
    const int set_flag = 1; 
    int   sockfd; 

    if (udpsocket) { 
     memset(udpsocket, 0, sizeof *udpsocket); 
     udpsocket->broadcast.sin_family = AF_INET; 
     udpsocket->broadcast.sin_addr.s_addr = INADDR_BROADCAST; 
     if (port >= 1 && port <= 65535) 
      udpsocket->broadcast.sin_port = htons(port); 
     udpsocket->descriptor = -1; 
    } 

    if (!udpsocket || interface_len < 1 || port < 1 || port > 65535) 
     return errno = EINVAL; 

    /* Generic UDP socket. */ 
    sockfd = socket(AF_INET, SOCK_DGRAM, 0); 
    if (sockfd == -1) 
     return errno; 

    /* Set SO_REUSEADDR if possible. */ 
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set_flag, sizeof set_flag); 

    /* Set IP_FREEBIND if possible. */ 
    setsockopt(sockfd, IPPROTO_IP, IP_FREEBIND, &set_flag, sizeof set_flag); 

    /* We need broadcast capability. */ 
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &set_flag, sizeof set_flag) == -1) { 
     const int real_errno = errno; 
     close(sockfd); 
     return errno = real_errno; 
    } 

    /* We want the IP_PKTINFO ancillary messages, to determine target address 
    * and interface index. */ 
    if (setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set_flag, sizeof set_flag) == -1) { 
     const int real_errno = errno; 
     close(sockfd); 
     return errno = real_errno; 
    } 

    /* We bind to the broadcast address. */ 
    if (bind(sockfd, (const struct sockaddr *)&(udpsocket->broadcast), sizeof udpsocket->broadcast) == -1) { 
     const int real_errno = errno; 
     close(sockfd); 
     return errno = real_errno; 
    } 

    /* Finally, we bind to the specified interface. */ 
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, interface, interface_len) == -1) { 
     const int real_errno = errno; 
     close(sockfd); 
     return errno = real_errno; 
    } 

    udpsocket->descriptor = sockfd; 

    udpsocket->if_index = if_nametoindex(interface); 

    errno = 0; 
    return 0; 
} 

main.c:

#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <signal.h> 
#include <stdio.h> 
#include <netdb.h> 
#include <errno.h> 
#include "privileges.h" 
#include "udp-broadcast.h" 

static volatile sig_atomic_t done_triggered = 0; 
static volatile sig_atomic_t reload_triggered = 0; 

static void done_handler(int signum) 
{ 
    __sync_bool_compare_and_swap(&done_triggered, (sig_atomic_t)0, (sig_atomic_t)signum); 
} 

static void reload_handler(int signum) 
{ 
    __sync_bool_compare_and_swap(&reload_triggered, (sig_atomic_t)0, (sig_atomic_t)signum); 
} 

static int install_handler(const int signum, void (*handler)(int)) 
{ 
    struct sigaction act; 
    memset(&act, 0, sizeof act); 
    sigemptyset(&act.sa_mask); 
    act.sa_handler = handler; 
    act.sa_flags = 0; 
    if (sigaction(signum, &act, NULL) == -1) 
     return errno; 
    return 0; 
} 

/* Return 0 if done_triggered or reload_triggered, nonzero otherwise. 
* Always clears reload_triggered. 
*/ 
static inline int keep_running(void) 
{ 
    if (done_triggered) 
     return 0; 
    return !__sync_fetch_and_and(&reload_triggered, (sig_atomic_t)0); 
} 

static const char *ipv4_address(const void *const addr) 
{ 
    static char buffer[16]; 
    char   *end = buffer + sizeof buffer; 
    unsigned char byte[4]; 

    if (!addr) 
     return "(none)"; 

    memcpy(byte, addr, 4); 

    *(--end) = '\0'; 
    do { 
     *(--end) = '0' + (byte[3] % 10); 
     byte[3] /= 10U; 
    } while (byte[3]); 
    *(--end) = '.'; 
    do { 
     *(--end) = '0' + (byte[2] % 10); 
     byte[2] /= 10U; 
    } while (byte[2]); 
    *(--end) = '.'; 
    do { 
     *(--end) = '0' + (byte[1] % 10); 
     byte[1] /= 10U; 
    } while (byte[1]); 
    *(--end) = '.'; 
    do { 
     *(--end) = '0' + (byte[0] % 10); 
     byte[0] /= 10U; 
    } while (byte[0]); 

    return (const char *)end; 
} 

int main(int argc, char *argv[]) 
{ 
    int port; 
    char dummy; 

    /* Check usage. */ 
    if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 
     fprintf(stderr, "\n"); 
     fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]); 
     fprintf(stderr, "  %s USERNAME INTERFACE PORT\n", argv[0]); 
     fprintf(stderr, "Where:\n"); 
     fprintf(stderr, "  USERNAME is the unprivileged user to run as,\n"); 
     fprintf(stderr, "  INTERFACE is the interface to bind to, and\n"); 
     fprintf(stderr, "  PORT  is the UDP/IPv4 port number to use.\n"); 
     fprintf(stderr, "\n"); 
     return EXIT_FAILURE; 
    } 

    /* Parse the port into a number. */ 
    if (sscanf(argv[3], "%d %c", &port, &dummy) != 1 || port < 1 || port > 65535) { 
     struct servent *serv = getservbyname(argv[3], "udp"); 
     if (serv && serv->s_port > 1 && serv->s_port < 65536) { 
      port = serv->s_port; 
      endservent(); 
     } else { 
      endservent(); 
      fprintf(stderr, "%s: Invalid port.\n", argv[3]); 
      return EXIT_FAILURE; 
     } 
    } 

    /* Drop privileges. */ 
    if (drop_privileges(argv[1], NEED_CAP_NET_RAW)) { 
     fprintf(stderr, "%s.\n", strerror(errno)); 
     return EXIT_FAILURE; 
    } 

    /* Install signal handlers. */ 
    if (install_handler(SIGINT, done_handler) || 
     install_handler(SIGTERM, done_handler) || 
     install_handler(SIGHUP, reload_handler) || 
     install_handler(SIGUSR1, reload_handler)) { 
     fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno)); 
     return EXIT_FAILURE; 
    } 

    fprintf(stderr, "Send a SIGINT (Ctrl+C) or SIGTERM to stop the service:\n"); 
    fprintf(stderr, "\tkill -SIGTERM %ld\n", (long)getpid()); 
    fprintf(stderr, "Send a SIGHUP or SIGUSR1 to have the service reload and rebroadcast:\n"); 
    fprintf(stderr, "\tkill -SIGHUP %ld\n", (long)getpid()); 
    fprintf(stderr, "Privileges dropped successfully.\n\n"); 
    fflush(stderr); 

    while (!done_triggered) { 
     struct udp_socket s; 

     if (open_udp_broadcast(&s, argv[2], port)) { 
      fprintf(stderr, "%s port %s: %s.\n", argv[2], argv[3], strerror(errno)); 
      return EXIT_FAILURE; 
     } 

     if (udp_broadcast(&s, "Hello?", 6, MSG_NOSIGNAL)) { 
      fprintf(stderr, "%s port %s: Broadcast failed: %s.\n", argv[2], argv[3], strerror(errno)); 
      close(s.descriptor); 
      return EXIT_FAILURE; 
     } 

     if (s.if_index) 
      fprintf(stderr, "Broadcast sent using interface %s (index %u); waiting for responses.\n", argv[2], s.if_index); 
     else 
      fprintf(stderr, "Broadcast sent using interface %s; waiting for responses.\n", argv[2]); 
     fflush(stderr); 

     while (keep_running()) { 
      struct sockaddr_in from_addr, to_addr, hdr_addr; 
      unsigned char  data[512]; 
      unsigned int  if_index; 
      size_t    size, i; 

      size = udp_receive(&s, data, sizeof data, 0, &from_addr, &to_addr, &hdr_addr, &if_index); 
      if (size > 0) { 
       printf("Received %zu bytes:", size); 
       for (i = 0; i < size; i++) 
        if (i & 15) 
         printf(" %02x", data[i]); 
        else 
         printf("\n\t%02x", data[i]); 
       if (if_index) 
        printf("\n\t Index: %u", if_index); 
       printf("\n\t From: %s", ipv4_address(&from_addr.sin_addr)); 
       printf("\n\t To: %s", ipv4_address(&to_addr.sin_addr)); 
       printf("\n\tHeader: %s", ipv4_address(&hdr_addr.sin_addr)); 
       printf("\n"); 
       fflush(stdout); 
      } else 
      if (errno != EINTR) { 
       fprintf(stderr, "%s\n", strerror(errno)); 
       break; 
      } 
     } 

     close(s.descriptor); 
    } 

    fprintf(stderr, "Exiting.\n"); 
    return EXIT_SUCCESS; 
} 

compilazione utilizzando

gcc -Wall -Wextra -O2 -c privileges.c 
gcc -Wall -Wextra -O2 -c udp-broadcast.c 
gcc -Wall -Wextra -O2 -c main.c 
gcc -Wall -Wextra main.o udp-broadcast.o privileges.o -lcap -o example 

ed eseguire il example come root, specificando un nome utente senza privilegi per l'esecuzione come l'interfaccia proteggere, e il numero di porta UDP come parametri:

sudo ./example yourdaemonuser eth0 4000 

In questo momento ho solo un laptop in uso, quindi il lato di ricezione è fondamentalmente non testato. So che CAP_NET_RAW è sufficiente qui (kernel Linux 4.2.0-27 su x86-64) e che le mandate di trasmissione UDP appaiono come in uscita dall'indirizzo dell'interfaccia ethernet a 255.255.255.255:port, ma non ho un'altra macchina da inviare esempio di risposte al demone (che sarebbe facile usando, ad esempio, NetCat: printf 'Response!' | nc -u4 -q2y interface-address port).

Si prega di notare che la qualità del codice sopra è solo grado di prova iniziale. Dal momento che non ho bisogno di questo da solo per nulla, e volevo solo verificare che non stavo parlando dal mio sedere, non ho speso alcuno sforzo per rendere il codice pulito o affidabile.

Domande? Commenti?

+0

Questo risponde a una grossa fetta della domanda. Il mio problema iniziale è tuttavia la necessità di ascoltare "INADDR_BROADCAST", sapere su quale interfaccia è stata ricevuta la trasmissione e utilizzare la stessa interfaccia per inviare a "INADDR_BROADCAST". E nei miei test, la capacità 'CAP_NET_RAW' non ha funzionato per' SO_BINDTODEVICE', solo il vero 'root' ha funzionato. Quindi in tal caso sarei in grado di rilasciare i privilegi solo dopo aver impostato questo sockopt, giusto? – TabascoEye

+0

@TabascoEye: indagherò (scriverò del codice di esempio e lo testerò da solo), e ti ricontatteremo su questo. –

+0

Hmm. Ho appena trovato questo nei sorgenti del kernel: http://lxr.free-electrons.com/source/net/core/sock.c#L569 quindi sembra che dovrebbe funzionare. Ma non nel mio semplice test di piccole dimensioni.Potrei semplicemente metterlo in una domanda a parte su StackOverflow – TabascoEye