Всем доброго времечка, не болеть и не дурнеть от СМИ!
Взялся я писать клиента на Пингвине, который получает бродкаст udp-датаграмму и посылает ответ таким же способом. Нужно это для того, чтобы удаленный девайс, который может находится в другой подсети, доступ к которому ограничен, смог словить пакет, и прислать свой в ответ со своим ip-адресом для дальнейшей с ним работы. Ну вот незадача - если говорить об SOCK_DGRAM, Linux стоит на страже любых поползновений, акромя тех, что в его сетке.
Общение проходит между Windows и Linux (я пишу для этой стороны).
Удалось протиснуться, установив опцию SO_BINDTODEVICE, хоть этот сокет начал отправлять, но этот сокет не принимает, а может лишь отправлять (насколько я понял из мануалов). То, что сработало, ниже:
int main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in their_addr; // connector's address information
struct hostent *he;
int numbytes;
int broadcast = 1;
if (argc != 3) {
fprintf(stderr,"usage: broadcaster hostname message\n");
exit(1);
}
if ((he=gethostbyname(argv[1])) == NULL) { // get the host info
perror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
}
// if ((sockfd = socket(AF_INET, SOCK_RAW, htons(ETH_P_IP))) == -1) {
// perror("socket");
// exit(1);
// }
// this call is what allows broadcast packets to be sent:
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) {
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "enp4s0", strlen("enp4s0")) == -1) {
perror("setsockopt (SO_BINDTODEVICE)");
exit(1);
}
their_addr.sin_family = AF_INET; // host byte order
their_addr.sin_port = htons(SERVERPORT); // short, network byte order
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,
(struct sockaddr *)&their_addr, sizeof their_addr)) == -1) {
perror("sendto");
exit(1);
}
printf("sent %d bytes to %s\n", numbytes,
inet_ntoa(their_addr.sin_addr));
close(sockfd);
Далее мои размышления пошли в сторону RAW-сокетов, что-то вроде этого
socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))
Как прочитал из мануалов, сырые сокеты могут получать датаграму еще до ее попадания в ядро, таким образом есть возможность отловить этот броадкаст, и вот моя следующая попытка это сделать:
int fd;
int bcast = 1;
char recvString[MAXRECVSTRING+1]; /* Buffer for received string */
int recvStringLen; /* Length of received string */
/* Create a best-effort datagram socket using UDP */
if ((fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0)
printf("socket() failed");
struct sockaddr_ll sock;
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_IP);
sock.sll_ifindex = 0;
sock.sll_hatype;
sock.sll_pkttype = PACKET_BROADCAST;;
sock.sll_halen;
sock.sll_addr[8];
memset(sock.sll_addr, '\0', sizeof(sock.sll_addr));
if (-1 == bind(fd, (struct sockaddr *) &sock, sizeof(sock)))
{
perror("bind");
close(fd);
exit(1);
}
if (-1 == setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast))) {
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
while (true)
{
/* Receive a single datagram from the server */
if ((recvStringLen = recvfrom(fd, recvString, MAXRECVSTRING, 0, NULL, 0)) < 0)
printf("recvfrom() failed");
recvString[recvStringLen] = '\0';
printf("Received: %s\n", recvString); /* Print the received string */
}
close(fd);
Только ОНО всё равно не работает, как надо: летит куча пакетов с первый байтом ‘E’. На просторах репозиториев находил, как люди устанавливают фильтры, но там жуткая морзянка из макросов, которую не каждый смелый человек отважится искать по крупицам. С posix сокетами раньше я не работал, использовал curl или qt, но нужда приперла. Прошу помочь навести порядок (что с чем мешать) с этими сокетами.