2016-02-01 33 views
5

tôi cần phải tái tạo một dịch vụ trên Linux được sử dụng để chạy trên một hệ thống nhúng chạy LwIP stack (nhẹ IP).Làm thế nào để tạo ra một dịch vụ gửi/nhận được chương trình phát sóng UDP trên nhiều giao diện

Dịch vụ đang sử dụng chương trình phát sóng UDP tới INADDR_BROADCAST (255.255.255.255) để tìm và định cấu hình thiết bị trên cùng một mạng con vật lý. Nó sẽ gửi một "quét" và tất cả các thiết bị chạy dịch vụ này trả lời với thiết lập mạng đầy đủ của họ (tất cả các NIC, tất cả các máy MAC và IP của họ). Sau đó, người dùng nhận được danh sách các thiết bị đó và có thể thay đổi thiết lập IP (sử dụng giao thức đã tồn tại).
[vâng, tôi biết mọi người sử dụng DHCP cho việc này, nhưng chúng tôi đang nói về lĩnh vực công nghiệp ở đây và giao thức/dịch vụ đã tồn tại, vì vậy tôi không có lựa chọn nào khác ngoài thực hiện điều gì đó tương thích]

Vì thiết bị có nhiều NIC, tôi cần có thể nhận được chương trình phát sóng này, biết NIC nào đã nhận và gửi trả lời qua NIC đó. Dịch vụ cũng có thể cấu hình để nó không mở ổ cắm trên các NIC cụ thể.

Ngăn xếp LwIP không phức tạp như chồng Linux, do đó, một ổ cắm gắn với IP vẫn nhận được tất cả các gói đến INADDR_BROADCAST. Vì vậy, nó đã được khá thẳng về phía trước để thực hiện điều này.

Trên Linux, tôi figured tôi có nhiều lựa chọn để làm điều này:

  • mở ổ cắm riêng cho mỗi NIC với SO_BROADCASTSO_BINDTODEVICE, vì vậy tôi có thể bind() họ INADDR_ANY và nhận chương trình phát sóng. Khi tôi gửi thư trả lời qua ổ cắm đó, định tuyến Linux bị bỏ qua và nó được gửi qua NIC mong muốn.
    NHƯNG: Tôi muốn các dịch vụ để không chạy như root ...
  • Có một đơn INADDR_ANY ổ cắm ràng buộc (có thể với IP_PKTINFO dễ dàng biết được NIC gói đến trên), đã một ổ cắm mỗi NIC, ràng buộc đến một địa chỉ hợp lệ, với SO_BROADCAST và gửi trả lời qua địa chỉ đó. Nếu tôi đi theo cách này tôi muốn chắc chắn rằng các ổ cắm gửi không bao giờ có thể nhận được bất cứ điều gì (bởi vì tôi không bao giờ gọi recv() trên chúng. Tài nguyên đói?).
    Có lẽ SO_RCVBUFSIZE = 0 sẽ là đủ?

Cách chính xác để triển khai điều này là gì?

+0

Rất khó trả lời đơn giản mà không cần phân tích thích hợp. Cố gắng tạo mokeup đơn giản để hoàn thành phân tích của bạn. (Tôi không biết trong lĩnh vực công nghiệp dịch vụ này đang chạy, nhưng tôi sợ về bảo mật ...) – rom1nux

+0

giao thức dành cho thiết lập ban đầu và không cho phép bạn thay đổi thiết lập IP khi hệ thống đang ở chế độ "chạy". – TabascoEye

+0

Tôi nghĩ rằng một mức tối thiểu im lặng làm cho 'SO_RCVBUFSIZE = 0' không hữu ích. Bạn có thể sử dụng 'shutdown (sockfd, SHUT_RD)' để vô hiệu hóa đọc thêm, nhưng tôi muốn nghiêng chương trình viết chính xác :) –

Trả lời

2

Bạn có thể cài đặt nhị phân với CAP_NET_RAW (và CAP_NET_BIND_SERVICE nếu cổng ≤ 1024 được sử dụng); setcap 'cap_net_raw=ep' yourdaemon làm thư mục gốc. Đối với IP, SO_BROADCAST không yêu cầu bất kỳ khả năng nào (đặc biệt, CAP_NET_BROADCAST không được sử dụng cho IP).

(Đối với các khả năng chính xác cần thiết, xem ví dụ net/core/sock.c:sock_setbindtodevice(), net/core/sock.c:sock_setsockopt(), và include/net/sock.h:sock_set_flag() trong nguồn Linux kernel để xác minh.)

Tuy nhiên, daemon thường được bắt đầu như là người chủ. Ở đây, ở trên sẽ không đủ, như thay đổi ID người dùng cho quá trình (để thả đặc quyền) cũng clears the effective capabilities. Tuy nhiên, tôi cũng thích dịch vụ của tôi để chạy với các đặc quyền hạn chế.

tôi sẽ lựa chọn giữa hai cách tiếp cận cơ bản:

  1. Yêu cầu daemon được thực hiện bởi root, hoặc với CAP_NET_RAW (và tùy chọn CAP_NET_BIND_SERVICE) khả năng.

    Sử dụng prctl(), setgroups() hoặc initgroups(), setresuid(), setresgid(), và khả năng từ libcap, cap_init(), cap_set_flag(), và cap_set_proc() giảm đặc quyền bằng cách chuyển sang một người sử dụng chuyên dụng và nhóm, nhưng giữ lại CAP_NET_RAW (và tùy chọn CAP_NET_BIND_SERVICE) và họ chỉ .

    Điều này cho phép daemon phản hồi, ví dụ: Tín hiệu HUP không khởi động lại hoàn toàn, vì nó có các đặc quyền cần thiết để liệt kê các giao diện và đọc các tệp cấu hình riêng của nó để mở các ổ cắm cho các giao diện mới.

  2. Sử dụng trình nạp "đặc quyền", mở tất cả các ổ cắm cần thiết, giảm đặc quyền và thực thi daemon thực.

    Daemon sẽ nhận được chi tiết giao diện ổ cắm và giao diện dưới dạng tham số dòng lệnh hoặc có thể thông qua đầu vào tiêu chuẩn. Daemon là hoàn toàn không có đặc quyền.

    Thật không may, nếu giao diện mới được mở hoặc cấu hình bị thay đổi, daemon không thể thực hiện nhiều ngoại trừ thoát. (Nó thậm chí không thể thực thi trình tải đặc quyền, vì các đặc quyền đã bị loại bỏ.)

Cách tiếp cận đầu tiên phổ biến hơn và dễ thực hiện hơn trong thực tế; đặc biệt nếu daemon chỉ được thực thi bởi root. (Hãy nhớ rằng, daemon có thể đáp ứng với các thay đổi cấu hình, vì nó có các khả năng cần thiết nhưng không phải là đặc quyền root nói chung.) Tôi chỉ sử dụng phương pháp thứ hai cho các tệp nhị phân "hộp đen" mà tôi không tin tưởng.


Dưới đây là một số mã ví dụ.

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; 
} 

Compile sử dụng

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 

và chạy example như rễ, chỉ định một tên người dùng không có đặc quyền để chạy như, giao diện để ràng buộc vào, và số cổng UDP làm thông số:

sudo ./example yourdaemonuser eth0 4000 

Ngay bây giờ tôi chỉ có một máy tính xách tay được sử dụng, do đó, bên nhận về cơ bản chưa được kiểm tra. Tôi biết rằng CAP_NET_RAW là đủ ở đây (Linux kernel 4.2.0-27 trên x86-64), và rằng các phát sóng UDP gửi hiển thị như đi từ địa chỉ giao diện ethernet đến 255.255.255.255:port, nhưng tôi không có một máy khác để gửi ví dụ trả lời cho daemon (mà sẽ dễ dàng sử dụng ví dụ như NetCat: printf 'Response!' | nc -u4 -q2y interface-address port).

Xin lưu ý rằng chất lượng mã ở trên chỉ là lớp thử nghiệm ban đầu. Vì tôi không cần bản thân mình cho bất cứ điều gì, và chỉ muốn xác minh tôi không nói ra khỏi mông của tôi, tôi đã không dành bất kỳ nỗ lực nào trong việc làm cho mã sạch hay đáng tin cậy.

Câu hỏi? Bình luận?

+0

Điều này trả lời một câu hỏi lớn. Tuy nhiên, vấn đề ban đầu của tôi là cần phải nghe 'INADDR_BROADCAST', biết giao diện nào đã được nhận và sử dụng cùng giao diện đó để gửi tới' INADDR_BROADCAST'. Và trong các thử nghiệm của tôi, khả năng 'CAP_NET_RAW' không hoạt động đối với' SO_BINDTODEVICE' chỉ thực sự là 'root'. Vì vậy, trong trường hợp đó tôi sẽ chỉ có thể thả đặc quyền _after_ thiết lập sockopt này, phải không? – TabascoEye

+0

@TabascoEye: Tôi sẽ điều tra (viết một số mã ví dụ và tự mình kiểm tra), và liên hệ lại với bạn về vấn đề này. –

+0

Hmm. Tôi chỉ tìm thấy điều này trong các nguồn hạt nhân: http://lxr.free-electrons.com/source/net/core/sock.c#L569 vì vậy có vẻ như nó sẽ hoạt động. Nhưng nó không có trong thử nghiệm nhỏ đơn giản của tôi.Tôi có thể chỉ cần đặt nó vào một câu hỏi riêng biệt trên stackoverflow – TabascoEye

Các vấn đề liên quan